This article describes the performance testing results of Python 3.9 compared to Python 3.8. A total of 89 various benchmark tests were conducted on computers with the AMD Ryzen 7000 series and the 13th-generation of Intel Core processors for desktops, laptops or mini PCs.
All tests were run on Windows 11 computers using the pyperformance 1.0.9 library in Python 3.8.10 and Python 3.9.13 (both 64-bit versions).
AMD Ryzen 7000 Series Desktop Processor
The first part of testing involved a desktop computer with an AMD Ryzen 9 7900 processor, RAM DDR5, and an M.2 PCIe Gen4 NVMe drive. The table below shows the results of 89 tests performed using Python 3.8 (as a reference) and Python 3.9 on this device.
Benchmark | Python 3.8 | Python 3.9 |
---|---|---|
2to3 | 216 ms | 217 ms (not significant) |
async_generators | 231 ms | 221 ms (1.04x faster) |
async_tree_cpu_io_mixed | 683 ms | 682 ms (not significant) |
async_tree_io | 1.35 sec | 1.33 sec (1.01x faster) |
async_tree_memoization | 552 ms | 559 ms (not significant) |
async_tree_none | 456 ms | 479 ms (1.05x slower) |
asyncio_tcp | 650 ms | 619 ms (1.05x faster) |
asyncio_tcp_ssl | 1.84 sec | 1.76 sec (1.04x faster) |
bench_mp_pool | 55.1 ms | 56.7 ms (1.03x slower) |
bench_thread_pool | 727 us | 706 us (1.03x faster) |
chameleon | 7.19 ms | 6.03 ms (1.19x faster) |
chaos | 60.9 ms | 57.6 ms (1.06x faster) |
comprehensions | 14.3 us | 14.5 us (not significant) |
coroutines | 21.9 ms | 21.4 ms (1.02x faster) |
coverage | 25.4 ms | 25.5 ms (not significant) |
create_gc_cycles | 602 us | 635 us (1.05x slower) |
crypto_pyaes | 65.0 ms | 61.8 ms (1.05x faster) |
dask | 265 ms | 269 ms (1.02x slower) |
deepcopy | 252 us | 245 us (1.03x faster) |
deepcopy_memo | 29.0 us | 28.3 us (1.02x faster) |
deepcopy_reduce | 2.24 us | 2.19 us (1.02x faster) |
deltablue | 4.06 ms | 4.00 ms (1.01x faster) |
django_template | 29.4 ms | 28.4 ms (1.03x faster) |
docutils | 1.54 sec | 1.57 sec (1.02x slower) |
dulwich_log | 43.9 ms | 42.7 ms (1.03x faster) |
fannkuch | 264 ms | 263 ms (not significant) |
float | 67.4 ms | 66.6 ms (1.01x faster) |
gc_traversal | 1.43 ms | 1.20 ms (1.19x faster) |
generators | 33.5 ms | 32.8 ms (1.02x faster) |
genshi_text | 18.7 ms | 19.1 ms (1.02x slower) |
genshi_xml | 139 ms | 140 ms (1.08x faster) |
hexiom | 5.57 ms | 5.26 ms (1.06x faster) |
html5lib | 41.4 ms | 41.6 ms (not significant) |
json_dumps | 8.16 ms | 8.28 ms (1.02x slower) |
json_loads | 15.8 us | 14.0 us (1.13x faster) |
logging_format | 6.29 us | 6.22 us (1.01x faster) |
logging_silent | 104 ns | 104 ns (not significant) |
logging_simple | 5.92 us | 5.78 us (1.02x faster) |
mako | 9.58 ms | 9.41 ms (1.02x faster) |
mdp | 1.64 sec | 1.77 sec (1.08x slower) |
meteor_contest | 64.7 ms | 63.7 ms (1.02x faster) |
nbody | 76.8 ms | 80.7 ms (1.05x slower) |
nqueens | 60.8 ms | 57.7 ms (1.05x faster) |
pathlib | 81.5 ms | 80.5 ms (1.01x faster) |
pickle | 6.68 us | 6.98 us (1.04x slower) |
pickle_dict | 17.9 us | 17.9 us (not significant) |
pickle_list | 2.57 us | 2.49 us (1.03x faster) |
pickle_pure_python | 255 us | 250 us (1.02x faster) |
pidigits | 136 ms | 139 ms (1.02x slower) |
pprint_pformat | 902 ms | 900 ms (not significant) |
pyflate | 394 ms | 394 ms (not significant) |
python_startup | 17.8 ms | 17.7 ms (1.01x faster) |
python_startup_no_site | 13.4 ms | 13.2 ms (1.02x faster) |
raytrace | 272 ms | 267 ms (1.02x faster) |
regex_compile | 88.5 ms | 86.8 ms (1.02x faster) |
regex_dna | 116 ms | 106 ms (1.09x faster) |
regex_effbot | 1.90 ms | 1.81 ms (1.05x faster) |
regex_v8 | 15.1 ms | 14.9 ms (1.01x faster) |
richards | 38.3 ms | 38.0 ms (1.01x faster) |
richards_super | 47.1 ms | 48.5 ms (1.03x slower) |
scimark_fft | 212 ms | 205 ms (1.03x faster) |
scimark_lu | 86.3 ms | 85.4 ms (1.01x faster) |
scimark_monte_carlo | 58.1 ms | 57.9 ms (not significant) |
scimark_sor | 104 ms | 105 ms (not significant) |
scimark_sparse_mat_mult | 3.08 ms | 3.04 ms (1.01x faster) |
spectral_norm | 81.9 ms | 79.7 ms (1.03x faster) |
sqlalchemy_declarative | 72.5 ms | 72.4 ms (not significant) |
sqlalchemy_imperative | 8.39 ms | 8.55 ms (1.02x slower) |
sqlglot_normalize | 185 ms | 181 ms (1.02x faster) |
sqlglot_optimize | 35.0 ms | 34.6 ms (1.01x faster) |
sqlglot_parse | 1.17 ms | 1.20 ms (1.03x slower) |
sqlglot_transpile | 1.36 ms | 1.36 ms (not significant) |
sqlite_synth | 1.83 us | 1.68 us (1.09x faster) |
sympy_expand | 255 ms | 261 ms (1.03x slower) |
sympy_integrate | 12.3 ms | 12.8 ms (1.04x slower) |
sympy_str | 155 ms | 159 ms (1.02x slower) |
sympy_sum | 86.3 ms | 87.4 ms (1.01x slower) |
telco | 3.62 ms | 3.89 ms (1.07x slower) |
tomli_loads | 1.65 sec | 1.57 sec (1.05x faster) |
tornado_http | 101 ms | 101 ms (not significant) |
typing_runtime_protocols | 316 us | 301 us (1.05x faster) |
unpack_sequence | 34.3 ns | 35.0 ns (1.02x slower) |
unpickle | 9.60 us | 7.90 us (1.21x faster) |
unpickle_list | 2.70 us | 2.87 us (1.06x slower) |
unpickle_pure_python | 175 us | 171 us (1.02x faster) |
xml_etree_generate | 57.9 ms | 53.7 ms (1.08x faster) |
xml_etree_iterparse | 55.3 ms | 55.4 ms (not significant) |
xml_etree_parse | 85.7 ms | 84.1 ms (1.02x faster) |
xml_etree_process | 44.7 ms | 42.9 ms (1.04x faster) |
Result (geometric mean) | 1.02x faster |
The result shows that Python 3.9 has the best performance results over Python 3.8 in the following tests: unpickle (1.21x faster), chameleon (1.19x faster), gc_traversal (1.19x faster). However, you may notice a drop in performance in some tests, especially in genshi_xml (1.08x faster), mdp (1.08x slower), telco (1.07x slower).
Additionally, you can examine the performance differences between Python 3.9 and Python 3.8 based on benchmarks belonging to specific groups. The table below shows the geometric mean for benchmarks from separate groups for Python 3.9 compared to Python 3.8.
Group of benchmarks | Python 3.9 to Python 3.8 |
---|---|
apps | 1.03x faster |
asyncio | 1.01x slower |
math | 1.02x slower |
regex | 1.04x faster |
serialize | 1.03x faster |
startup | 1.01x faster |
template | 1.03x faster |
13th Gen Intel Core Mobile Processor
The second part of testing involved a mini PC with an Intel Core i3-1315U Processor (which is also used in laptops), RAM DDR4, and an M.2 PCIe Gen4 NVMe drive. The table below shows the results of 89 tests performed using Python 3.8 (as a reference) and Python 3.9 on this device.
Benchmark | Python 3.8 | Python 3.9 |
---|---|---|
2to3 | 286 ms | 281 ms (1.02x faster) |
async_generators | 255 ms | 249 ms (1.02x faster) |
async_tree_cpu_io_mixed | 709 ms | 708 ms (not significant) |
async_tree_io | 1.17 sec | 1.17 sec (not significant) |
async_tree_memoization | 574 ms | 583 ms (not significant) |
async_tree_none | 460 ms | 479 ms (1.04x slower) |
asyncio_tcp | 944 ms | 912 ms (1.04x faster) |
asyncio_tcp_ssl | 2.29 sec | 2.24 sec (1.02x faster) |
bench_mp_pool | 87.9 ms | 92.4 ms (1.05x slower) |
bench_thread_pool | 1.21 ms | 1.20 ms (not significant) |
chameleon | 11.6 ms | 6.93 ms (1.67x faster) |
chaos | 70.1 ms | 69.2 ms (1.01x faster) |
comprehensions | 17.8 us | 16.3 us (1.09x faster) |
coroutines | 28.2 ms | 25.4 ms (1.11x faster) |
coverage | 26.1 ms | 25.2 ms (1.03x faster) |
create_gc_cycles | 861 us | 883 us (1.03x slower) |
crypto_pyaes | 73.8 ms | 70.5 ms (1.05x faster) |
dask | 440 ms | 436 ms (not significant) |
deepcopy | 296 us | 288 us (1.03x faster) |
deepcopy_memo | 32.5 us | 31.2 us (1.04x faster) |
deepcopy_reduce | 2.53 us | 2.52 us (not significant) |
deltablue | 4.49 ms | 4.50 ms (not significant) |
django_template | 34.1 ms | 34.6 ms (not significant) |
docutils | 2.03 sec | 2.08 sec (1.02x slower) |
dulwich_log | 56.7 ms | 57.7 ms (1.02x slower) |
fannkuch | 297 ms | 289 ms (1.03x faster) |
float | 74.7 ms | 73.1 ms (1.02x faster) |
gc_traversal | 1.74 ms | 1.69 ms (1.02x faster) |
generators | 35.5 ms | 34.4 ms (1.03x faster) |
genshi_text | 21.3 ms | 21.1 ms (1.01x faster) |
genshi_xml | 160 ms | 157 ms (1.50x faster) |
hexiom | 6.07 ms | 5.86 ms (1.04x faster) |
html5lib | 55.5 ms | 56.1 ms (not significant) |
json_dumps | 9.12 ms | 8.96 ms (1.02x faster) |
json_loads | 22.6 us | 16.5 us (1.37x faster) |
logging_format | 7.35 us | 7.50 us (1.02x slower) |
logging_silent | 110 ns | 110 ns (not significant) |
logging_simple | 6.94 us | 6.97 us (not significant) |
mako | 11.0 ms | 10.5 ms (1.05x faster) |
mdp | 1.93 sec | 1.99 sec (1.03x slower) |
meteor_contest | 81.6 ms | 81.9 ms (not significant) |
nbody | 78.6 ms | 81.5 ms (1.04x slower) |
nqueens | 73.5 ms | 69.8 ms (1.05x faster) |
pathlib | 75.3 ms | 74.6 ms (not significant) |
pickle | 7.56 us | 7.89 us (1.04x slower) |
pickle_dict | 20.0 us | 20.0 us (not significant) |
pickle_list | 3.07 us | 3.01 us (1.02x faster) |
pickle_pure_python | 295 us | 292 us (1.01x faster) |
pidigits | 167 ms | 170 ms (1.02x slower) |
pprint_pformat | 1.06 sec | 1.05 sec (1.01x faster) |
pyflate | 471 ms | 471 ms (not significant) |
python_startup | 25.1 ms | 25.1 ms (not significant) |
python_startup_no_site | 19.9 ms | 19.8 ms (not significant) |
raytrace | 304 ms | 301 ms (1.01x faster) |
regex_compile | 112 ms | 112 ms (not significant) |
regex_dna | 153 ms | 147 ms (1.04x faster) |
regex_effbot | 1.86 ms | 1.78 ms (1.04x faster) |
regex_v8 | 17.1 ms | 16.5 ms (1.04x faster) |
richards | 42.5 ms | 41.1 ms (1.04x faster) |
richards_super | 53.2 ms | 51.7 ms (1.03x faster) |
scimark_fft | 212 ms | 211 ms (not significant) |
scimark_lu | 88.4 ms | 87.0 ms (1.02x faster) |
scimark_monte_carlo | 66.7 ms | 65.7 ms (1.02x faster) |
scimark_sor | 114 ms | 111 ms (1.03x faster) |
scimark_sparse_mat_mult | 2.99 ms | 2.95 ms (1.01x faster) |
spectral_norm | 86.6 ms | 86.0 ms (1.01x faster) |
sqlalchemy_declarative | 115 ms | 115 ms (not significant) |
sqlalchemy_imperative | 13.0 ms | 13.3 ms (1.02x slower) |
sqlglot_normalize | 217 ms | 207 ms (1.05x faster) |
sqlglot_optimize | 41.3 ms | 41.0 ms (1.01x faster) |
sqlglot_parse | 1.39 ms | 1.38 ms (1.01x faster) |
sqlglot_transpile | 1.64 ms | 1.62 ms (1.02x faster) |
sqlite_synth | 2.35 us | 2.26 us (1.04x faster) |
sympy_expand | 336 ms | 352 ms (1.05x slower) |
sympy_integrate | 16.6 ms | 16.7 ms (1.01x slower) |
sympy_str | 207 ms | 212 ms (1.02x slower) |
sympy_sum | 116 ms | 115 ms (not significant) |
telco | 4.24 ms | 4.38 ms (1.03x slower) |
tomli_loads | 1.78 sec | 1.75 sec (1.02x faster) |
tornado_http | 140 ms | 142 ms (1.01x slower) |
typing_runtime_protocols | 374 us | 361 us (1.04x faster) |
unpack_sequence | 39.6 ns | 40.9 ns (1.03x slower) |
unpickle | 10.7 us | 9.05 us (1.18x faster) |
unpickle_list | 2.98 us | 2.99 us (not significant) |
unpickle_pure_python | 233 us | 199 us (1.17x faster) |
xml_etree_generate | 73.5 ms | 59.5 ms (1.24x faster) |
xml_etree_iterparse | 75.3 ms | 69.7 ms (1.08x faster) |
xml_etree_parse | 122 ms | 113 ms (1.08x faster) |
xml_etree_process | 57.7 ms | 54.6 ms (1.06x faster) |
Result (geometric mean) | 1.03x faster |
The result shows that Python 3.9 has the best performance results over Python 3.8 in the following tests: chameleon (1.67x faster), genshi_xml (1.50x faster), json_loads (1.37x faster). However, you may notice a drop in performance in some tests, especially in bench_mp_pool (1.05x slower), sympy_expand (1.05x slower), async_tree_none (1.04x slower).
Additionally, you can examine the performance differences between Python 3.9 and Python 3.8 based on benchmarks belonging to specific groups. The table below shows the geometric mean for benchmarks from separate groups for Python 3.9 compared to Python 3.8.
Group of benchmarks | Python 3.9 to Python 3.8 |
---|---|
apps | 1.10x faster |
asyncio | 1.01x slower |
math | 1.01x slower |
regex | 1.03x faster |
serialize | 1.08x faster |
template | 1.12x faster |