cpython
cpython copied to clipboard
gh-99305: `secrets.token_hex()` speeded up 2x
- Issue: gh-99305
Most changes to Python require a NEWS entry.
Please add it using the blurb_it web app or the blurb command-line tool.
@NewUserHa Can you sign the CLA and write a news entry? The improvement looks good
Done.
The speed improvement depends on a bit on the number of bytes requested. A benchmark also taking the token_bytes into account:
nbytes=2048
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 3.5 µs ± 93.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# 3 µs ± 106 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
nbytes=32
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 574 ns ± 62.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
# 426 ns ± 41.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
nbytes=1
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 414 ns ± 16.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
# 281 ns ± 2.71 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
right, taking token_bytes into account is only 1.2~1.4 time faster.
on my device
nbytes=2048
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 4.86 µs ± 449 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# 3.93 µs ± 87.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# 1.2366412213740458015267175572519 faster
nbytes=32
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 743 ns ± 66.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 528 ns ± 28 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 1.407196969696969696969696969697 faster
nbytes=1
%timeit binascii.hexlify(token_bytes(nbytes)).decode('ascii')
%timeit token_bytes(nbytes).hex()
# 652 ns ± 80.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 442 ns ± 53.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 1.2366412213740458015267175572519 faster
t = os.urandom(2048)
%timeit binascii.hexlify(t).decode('ascii')
%timeit t.hex()
# 3.69 µs ± 579 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# 2.78 µs ± 349 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# 1.3273381294964028776978417266187 faster
t = os.urandom(32)
%timeit binascii.hexlify(t).decode('ascii')
%timeit t.hex()
# 323 ns ± 112 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 116 ns ± 3.33 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
# 2.7844827586206896551724137931034 faster
t = os.urandom(1)
%timeit binascii.hexlify(t).decode('ascii')
%timeit t.hex()
# 221 ns ± 13.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# 77.4 ns ± 1.97 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
# 2.8552971576227390180878552971576 faster
https://github.com/python/cpython/blob/cb04a09d2dfd197436a11de504b92773569e19fb/Objects/bytesobject.c#L2482
https://github.com/python/cpython/blob/f07adf82f338ebb7e69475537be050e63c2009fa/Modules/clinic/binascii.c.h#L486
binascii.hexlify() works the same with bytes.hex() + encode() but has different implements. merging may get another free performance boost that can faster python.
import binascii can be removed from the module probably
right, import binascii removed.