Uh oh!
There was an error while loading. Please reload this page.
gh-128150: improve performances of uuid.uuid* constructor functions. - #128151
Conversation
eendebakpt
commented
Dec 21, 2024
The changes itself look good at first glance. On the other hand: if performance is really important, there there dedicated packages to calculate uuids (binding to rust or C) that are much faster. One more idea to improve performance: add a dedicated constructor that skips the checks. For example add to Results in (the |
picnixz
commented
Dec 21, 2024
I also thought about expanding the C interface for the module but it would have been too complex as a first iteration. As for third-party packages, I do know about them but there might be slightly differences in which methods they use for the UUID (and this could be a stop for existing code, namely switching to another implementation).
I also had this idea but haven't tested it as a first iteration. I wanted to get some feedback (I feel that performance gains are fine but OTOH, the code is a bit uglier =/) |
4f2744a to
0710549Comparepicnixz
commented
Dec 21, 2024
Ok the benchmarks are not always very stable but I do see improvements sith the dedicated constructor. I need to go now but I'll try to see which version is the best and the most stable. |
So, we're now stable and consistent: Strictly speaking, the uuid1() benchmarks can be considered significant but only if you consider a 4% improvement as significant, which I did not. I only kept improvements over 10%. The last column is the same as the second one (PGO, no LTO) but using |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
eendebakpt
left a comment
There was a problem hiding this comment.
Nice improvement overall! Personally I am not a fan of the lazy imports here, but I'll let someone else decide on that.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
picnixz
commented
Dec 23, 2024
The entire module has been written so to reduce import times but I understand. I'll adress your comments tomorrow and will also check if I can remove some unnecessary micro optimizations. Thank you! |
In this commit, we move the rationale for using HACL*-based MD5 instead of its OpenSSL implementation from the code to this note. HACL*-based MD5 is 2x faster than its OpenSSL implementation for creating the hash object via `h = md5(..., usedforsecurity=False)` but `h.digest()` is slightly (yet noticeably) slower. Overall, HACL*-based MD5 still remains faster than its OpenSSL-based implementation, whence the choice of `_md5.md5` over `hashlib.md5`.
In this commit, we move the rationale for using OpenSSL-based SHA-1 instead of its HACL* implementation from the code to this note. HACL*-based SHA-1 is 2x faster than its OpenSSL implementation for creating the hash object via `h = sha1(..., usedforsecurity=False)` but `h.digest()` is almost 3x slower. Unlike HACL* MD5, HACL*-based SHA-1 is slower than its OpenSSL-based implementation, whence the choice of `hashlib.sha1` over `_sha1.sha1`.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Not sure what happens but I'm seeing slow downs. How can I check that constant folding was done? EDIT: I'll regenerate the benchmarks to be sure. Wait a bit. |
picnixz
commented
Dec 27, 2024
Here are the final benchmarks: We are indeed faster. Note that with a manual constant folding, I also have the same numbers (I just regenerated everything from scratch). I think sometimes we have noise. I'll update the NEWS as well to reflect the latest numbers. |
Uh oh!
There was an error while loading. Please reload this page.
picnixz
commented
Jan 12, 2025
Replacing On main it is: With fallback MD5, we're a bit faster for small lengths: I think I can live with |
There was a problem hiding this comment.
Since we're always bundling
HACL*MD5 implementation, I wondered whether we could just use it. Or do you think users would prefer if we usehashlibexplicitly (and OpenSSL when available)?
It's possible to build Python without HACL*, mainly for environments where cryptography is somehow regulated (i.e. the infamous FIPS mode).
It might be OK to use HACL* by default in hashlib, possibly making it a bit faster for everyone (who didn't opt out).
But UUID should, IMO, use the default. We agree on that, hit the green button :)
picnixz
commented
Jan 13, 2025
Ah, I think I forgot about this one because I was recently working on HMAC and I didn't give the possibility to avoid HMAC-MD5 for instance.
:) |
I've just updated the NEWS entry (we have 30% gain if we only focus on 3, 4, 5 and 8, and a 20% gain if we include the benchmarks of UUID1(), but since the NEWS entry does not talk about version 1 (performance are the same), we should report the correct numbers) |
There are some points that can be addressed:
We can drop some micro-optimizations to reduce the diff. Most of the time is taken by function calls and loading integers.
HACL* MD5 is faster than OpenSSL MD5 so it's better to use the former. However, usingFor consistency, we'll rely on OpenSSL-based implementation even if it's a bit slower._md5.md5orfrom _md5 import md5is a micro-optimization that can be dropped without affecting performances too much (see cff86e9 and 7095aa4)The rationale of expandingnot 0 <= x < 1 << 128intox < 0 or x > 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffis due to the non-equivalent bytecodes.Similar arguments apply to expanding
not 0 <= x < (1 << C)intox < 0 or x > Bwhere B is the hardcoded hexadecimal value of(1 << C) - 1.Bytecode comparisons (not useful, constant folding will make them similarly performant)
versus
uuid.*functions #128150📚 Documentation preview 📚: https://cpython-previews--128151.org.readthedocs.build/