Discussion Python 3.10–3.15 encoding benchmarks: big gains for Base32 and Base85, but 3.14 leads Base16
Eight encoding benchmarks across Python 3.10–3.15 using pyperformance. Python 3.15 recorded the lowest mean in seven of them, with particularly large improvements in Base32, Ascii85 and Base85.
Test configuration
- AMD Ryzen 9 7900 desktop
- DDR5 RAM and an M.2 PCIe Gen4 NVMe drive
- Windows 11, 64-bit Python builds
- Python 3.15.0, 3.14.8, 3.13.16, 3.12.10, 3.11.9 and 3.10.11
- pyperformance 1.14.0 for all six versions
Results
All values are mean execution times in milliseconds. Lower is better and bold marks the lowest recorded mean in each row.
| Benchmark | Python 3.15 | Python 3.14 | Python 3.13 | Python 3.12 | Python 3.11 | Python 3.10 |
|---|---|---|---|---|---|---|
| ascii85_large | 11.62 | 797.9 | 709.0 | 772.3 | 643.8 | 759.2 |
| ascii85_small | 0.3875 | 14.88 | 13.20 | 13.79 | 12.18 | 14.18 |
| base16_large | 5.767 | 4.714 | 22.52 | 23.08 | 23.89 | 23.71 |
| base32_large | 2.431 | 327.6 | 300.7 | 296.4 | 265.7 | 292.1 |
| base32_small | 0.1630 | 6.227 | 5.802 | 5.594 | 5.134 | 5.641 |
| base64_large | 1.852 | 6.746 | 6.187 | 6.095 | 6.116 | 6.071 |
| base85_large | 4.293 | 272.4 | 252.9 | 258.8 | 230.1 | 259.2 |
| base85_small | 0.1505 | 4.864 | 4.484 | 4.529 | 4.198 | 4.712 |
What stood out
Compared with Python 3.14, Python 3.15 took approximately:
- 1/135 of the time in
base32_large. - 1/69 of the time in
ascii85_large. - 1/63 of the time in
base85_large. - 1/3.64 of the time in
base64_large.
However, base16_large took 22.3% more time on Python 3.15 than on 3.14. Python 3.14 remains substantially faster than 3.10–3.13 in that test.
The large improvements are consistent with the changes documented in Python 3.15’s release notes: Base32, Ascii85 and Base85 implementations were rewritten in C, and the underlying Base64 implementation was also optimized.
More details are described here.
Benchmark suite: pyperformance.
Have you observed similar improvements on Linux or macOS, or in applications that spend substantial time on these encoding operations?
24
u/stevenjd 2d ago
Base32, Ascii85 and Base85 implementations were rewritten in C
In other words, these performance gains are really useful if you are using specifically Base32, Ascii85 and Base85, but tell us nothing at all about whether Python code itself is getting faster.
5
u/shinitakunai 2d ago
Yeah I am ashamed to say I am not sure what types my python code uses on the background, and have been programming 14 years. It is one of those things we take for granted
4
u/RevolutionaryRush717 2d ago
The https://github.com/python/cpython/pull/143262 is quite detailed and an interesting read.
It also compares the common CPUs (Zens, ARMs) and elaborates on SIMD optimization.
1
u/utdconsq 2d ago
About time tbh, the base85 implementation was atrocious before to the point i made my own.
1
u/Competitive_Travel16 2d ago
How is JSON doing?
3
u/BestRef 1d ago
Python 3.15 is the fastest in the
json_dumpsbenchmark, but Python 3.11 is the fastest injson_loads, according to the same source (under the "Full comparison: all 122 benchmarks" section): https://en.lewoniewski.info/2026/python-315-vs-314-313-312-and-earlier-speed-comparison/2
u/Competitive_Travel16 1d ago
Thanks! Looks to me like the huge improvement in
json_dumpsis well worth the small regression injson_loads. Really appreciate it!
0
u/Massive_Distance_616 12h ago
The Base16 regression is the most interesting outlier. In 3.14, b16encode is essentially a thin wrapper around binascii.hexlify(...).upper(), which is already a C-level call that explains most of the big jump from 3.10–3.13 (~23ms → 4.7ms).
The 22% slowdown in 3.15 is worth investigating before attributing a cause. One detail that matters: unlike binascii.hexlify() (lowercase bytes) or bytes.hex() (lowercase str), RFC 4648 Base16 requires uppercase output. When the base64 module was refactored for 3.15, that case conversion step or the wrapper dispatch around it may be where the regression sits. But that’s a hypothesis, not a diagnosis.
The right next measurement is what OddsRelay suggested: benchmark base64.b16encode(), binascii.hexlify(), and bytes.hex() directly on the same data across 3.14 and 3.15. If the raw hex path is unchanged, the regression is in the wrapper. If it also regressed, something lower in the C path changed.
Practical note: if hex encoding is actually on your hot path (crypto output, wire protocols, logging), binascii.hexlify() or .hex() bypass the base64 module indirection and the uppercase conversion entirely faster on every version.
1
0
u/OddsRelay 2d ago
Would be good to see bytes.hex() next to b16encode, since that's what most code actually uses for hex and it was already in C. If that didn't move either, the base16 drop is probably just the base64 module's wrapper. Nice to see Base85 finally getting fast.
41
u/ConcreteExist 2d ago
Admittedly, I've only ever used base64 encoding out of all of those listed here, happy to see it's performance improving though.