Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Description
Feature or enhancement
The dedicated UUID constructors (e.g.,uuid.uuid4()) generate bytes and pass them to theUUID constructor. However, the latter performs multiple and redundant checks. We can by-pass those checks since we are actually creating manually the UUID object. Here are the benchmarks for a PGO andpython -OO (no LTO) build and a dedicatedUUID.from_int constructor:
+----------------------------------------+---------+-----------------------+| Benchmark | ref | final |+========================================+=========+=======================+| uuid1(node, None) | 1.20 us | 1.16 us: 1.04x faster |+----------------------------------------+---------+-----------------------+| uuid1(None, clock_seq) | 1.16 us | 1.14 us: 1.02x faster |+----------------------------------------+---------+-----------------------+| uuid3(NAMESPACE_DNS, os.urandom(16)) | 1.13 us | 809 ns: 1.40x faster |+----------------------------------------+---------+-----------------------+| uuid3(NAMESPACE_DNS, os.urandom(1024)) | 2.08 us | 1.73 us: 1.20x faster |+----------------------------------------+---------+-----------------------+| uuid4() | 1.16 us | 885 ns: 1.31x faster |+----------------------------------------+---------+-----------------------+| uuid5(NAMESPACE_DNS, os.urandom(16)) | 1.15 us | 832 ns: 1.39x faster |+----------------------------------------+---------+-----------------------+| uuid5(NAMESPACE_DNS, os.urandom(1024)) | 1.57 us | 1.27 us: 1.24x faster |+----------------------------------------+---------+-----------------------+| uuid8() | 952 ns | 694 ns: 1.37x faster |+----------------------------------------+---------+-----------------------+| Geometric mean | (ref) | 1.21x faster |+----------------------------------------+---------+-----------------------+Benchmark hidden because not significant (1): uuid1()+----------------------------------------+---------+-----------------------+| Benchmark | ref | final |+========================================+=========+=======================+| uuid3(NAMESPACE_DNS, os.urandom(16)) | 1.13 us | 809 ns: 1.40x faster |+----------------------------------------+---------+-----------------------+| uuid3(NAMESPACE_DNS, os.urandom(1024)) | 2.08 us | 1.73 us: 1.20x faster |+----------------------------------------+---------+-----------------------+| uuid4() | 1.16 us | 885 ns: 1.31x faster |+----------------------------------------+---------+-----------------------+| uuid5(NAMESPACE_DNS, os.urandom(16)) | 1.15 us | 832 ns: 1.39x faster |+----------------------------------------+---------+-----------------------+| uuid5(NAMESPACE_DNS, os.urandom(1024)) | 1.57 us | 1.27 us: 1.24x faster |+----------------------------------------+---------+-----------------------+| uuid8() | 952 ns | 694 ns: 1.37x faster |+----------------------------------------+---------+-----------------------+| Geometric mean | (ref) | 1.31x faster |+----------------------------------------+---------+-----------------------+Ignored benchmarks (3) of ref.json: uuid1(), uuid1(None, clock_seq), uuid1(node, None)The above benchmarks keep constants as is since constant folding would remove the inefficiency of recomputing1 << const everytime. With a hardcoded1 << const, the numbers are (almost) identical.
I did not change the UUIDv1 generation because I observed that it would be worse in theuuid.uuid1() form (but 50% faster when either the node or the clock sequence is given, but this is likely not the usual call form).
Benchmark script
importosimportrandomimportuuidimportpyperfif__name__=='__main__':runner=pyperf.Runner()runner.bench_func('uuid1()',uuid.uuid1)node=random.getrandbits(48)runner.bench_func('uuid1(node, None)',uuid.uuid1,node)clock_seq=random.getrandbits(14)runner.bench_func('uuid1(None, clock_seq)',uuid.uuid1,None,clock_seq)ns=uuid.NAMESPACE_DNSrunner.bench_func('uuid3(NAMESPACE_DNS, os.urandom(16))',uuid.uuid3,ns,os.urandom(16))runner.bench_func('uuid3(NAMESPACE_DNS, os.urandom(1024))',uuid.uuid3,ns,os.urandom(1024))runner.bench_func('uuid4()',uuid.uuid4)ns=uuid.NAMESPACE_DNSrunner.bench_func('uuid5(NAMESPACE_DNS, os.urandom(16))',uuid.uuid5,ns,os.urandom(16))runner.bench_func('uuid5(NAMESPACE_DNS, os.urandom(1024))',uuid.uuid5,ns,os.urandom(1024))runner.bench_func('uuid8()',uuid.uuid8)
I'll submit a PR and we can decide what to keep and what to remove for maintainibility purposes. Note that theuuid module has been improved a lot performance-wise especially in terms of import time but I believe that constructing UUIDs objects via their dedicated functions.