Skip to content

Conversation

@henryiii
Copy link
Contributor

@henryiii henryiii commented Nov 27, 2025

This makes the __str__ method faster - about 6-7% less time to do str(Version(v)); probably about 25% faster for just the str operation. This is used quite a bit in SpecifierSet, it makes a small but measurable improvement there. It's larger than the speedup you get if the generator is replaced by map #996, though you can get close by using the := trick as well. This is still a bit faster, and I think it reads better.

@henryiii henryiii force-pushed the henryiii/perf/str branch 2 times, most recently from 52925ec to 267b5d9 Compare November 27, 2025 15:31
Signed-off-by: Henry Schreiner <[email protected]>
@henryiii
Copy link
Contributor Author

henryiii commented Nov 27, 2025

Almost all the savings here was from avoiding the NamedTuple indirection. This is now only 1% faster total time, probably 5% or something like that faster for the str operation. Saving the intermediate value doesn't have any measurable effect anymore, so I've remove that.

Now it's mostly up to if you think this looks better (and it still is a little faster).

@brettcannon
Copy link
Member

Now it's mostly up to if you think this looks better

I'm indifferent.

@notatallshaw
Copy link
Member

"".join(...) reads better to me because I've written that pattern so often in Python, but that's just anecdotal.

I was also under the impression, apparently incorrectly, that join would be faster. Because naively concatenating strings can be O(n^2) with regards to memory allocation operations, and I thought the .join method had some kind of optimization to handle that. Maybe this is just too few concatenations with too small strings where the memory allocations become a dominating factor.

@henryiii
Copy link
Contributor Author

I'm nearly sure it's the fact the strings are generally small. If they were large I'm almost sure it would be the other way around.

I played around with several ways to do this - I thought making all four separately then using an f-string to join them would be fastest, but short circuiting if None was too important. Now that the largest cost (accessing the nested field in the NamedTuple) is gone, it's possible that is faster.

@brettcannon
Copy link
Member

I was also under the impression, apparently incorrectly, that join would be faster. Because naively concatenating strings can be O(n^2) with regards to memory allocation operations, and I thought the .join method had some kind of optimization to handle that. Maybe this is just too few concatenations with too small strings where the memory allocations become a dominating factor.

There's an optimization in CPython specifically for += in a loop. So you're right that str.join() should be faster, but we cheated in CPython. 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants