Python3.11が高速化しているかベンチマークしてみた

2022.10.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。CX事業本部Delivery部のakkyです。

2022年10月24日にPythonの最新版である3.11.0がリリースされました。 このリリースの目玉は実行速度の高速化です。

CPythonはいままで高速化にはあまり力を入れiませんでしたが、マイクロソフトの出資を受け、実行速度を4年間で5倍高速化するプロジェクトが始動しました。

Python3.11はその最初のリリースです。おおよそ1.2倍程度高速になっているとされていますが、実際のアプリケーションではどのような感じでしょうか? EC2で実験してみました。

検証環境

CPU情報

$ sudo lshw -class processor
  *-cpu                     
       description: CPU
       product: Intel(R) Xeon(R) Platinum 8252C CPU @ 3.80GHz
       vendor: Intel Corp.
       physical id: 4
       bus info: cpu@0
       version: 6.85.7
       slot: CPU 0
       size: 3800MHz
       width: 64 bits
       clock: 100MHz
       capabilities: lm fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp x86-64 constant_tsc rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves ida arat pku ospke avx512_vnni
       configuration: cores=1 enabledcores=1 microcode=83898890 threads=2

フィボナッチ数を計算する関数

Python3.11では、関数の呼び出しが高速化されているとのことで、まずはフィボナッチ数を計算する関数で実験しました。

def fibonacci(n):
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 2) + fibonacci(n - 1)

import sys
print(fibonacci(int(sys.argv[1])))

Wikipediaを参考にしました

Python3.10.6

$ time python3.10 fibo.py 40
102334155

real    0m24.325s
user    0m24.320s
sys     0m0.004s

Python3.11.0

$ time python3.11 fibo.py 40
102334155

real    0m12.557s
user    0m12.553s
sys     0m0.004s

24秒から12秒と、2倍の実行速度が得られました!すごい!

pyperformance

実際のワークロードに近いベンチマークを提供するpyperformanceでも検証してみましょう。

※pipの関係でpyperformanceが3.10のsite-packagesに入りましたが、比較には影響ないかと思います。

$ python3.11 -m pyperformance compare py310.json py311.json 
py310.json
==========

Performance version: 1.0.5
Report on Linux-5.15.0-1019-aws-x86_64-with-glibc2.35
Number of logical CPUs: 2
Start date: 2022-10-28 04:46:09.095860
End date: 2022-10-28 05:04:38.410011

py311.json
==========

Performance version: 1.0.5
Report on Linux-5.15.0-1019-aws-x86_64-with-glibc2.35
Number of logical CPUs: 2
Start date: 2022-10-28 04:20:13.505432
End date: 2022-10-28 04:36:28.061482

### 2to3 ###
Mean +- std dev: 298 ms +- 3 ms -> 225 ms +- 5 ms: 1.32x faster
Significant (t=96.12)

### chameleon ###
Mean +- std dev: 7.63 ms +- 0.05 ms -> 5.49 ms +- 0.08 ms: 1.39x faster
Significant (t=172.42)

### chaos ###
Mean +- std dev: 89.1 ms +- 0.8 ms -> 52.9 ms +- 0.5 ms: 1.68x faster
Significant (t=310.52)

### crypto_pyaes ###
Mean +- std dev: 95.4 ms +- 0.7 ms -> 59.4 ms +- 0.7 ms: 1.61x faster
Significant (t=268.86)

### deltablue ###
Mean +- std dev: 6.26 ms +- 0.12 ms -> 3.18 ms +- 0.04 ms: 1.97x faster
Significant (t=184.93)

### django_template ###
Mean +- std dev: 38.6 ms +- 0.4 ms -> 26.9 ms +- 0.4 ms: 1.43x faster
Significant (t=164.75)

### dulwich_log ###
Mean +- std dev: 63.2 ms +- 0.5 ms -> 53.7 ms +- 0.3 ms: 1.18x faster
Significant (t=122.83)

### fannkuch ###
Mean +- std dev: 411 ms +- 5 ms -> 300 ms +- 2 ms: 1.37x faster
Significant (t=152.91)

### float ###
Mean +- std dev: 97.2 ms +- 1.3 ms -> 64.3 ms +- 1.1 ms: 1.51x faster
Significant (t=146.12)

### genshi_text ###
Mean +- std dev: 24.9 ms +- 0.2 ms -> 18.0 ms +- 0.2 ms: 1.38x faster
Significant (t=185.05)

### genshi_xml ###
Mean +- std dev: 52.4 ms +- 0.6 ms -> 42.4 ms +- 0.5 ms: 1.24x faster
Significant (t=105.21)

### go ###
Mean +- std dev: 195 ms +- 1 ms -> 113 ms +- 1 ms: 1.73x faster
Significant (t=325.79)

### hexiom ###
Mean +- std dev: 7.86 ms +- 0.06 ms -> 5.19 ms +- 0.04 ms: 1.52x faster
Significant (t=274.95)

### html5lib ###
Mean +- std dev: 71.2 ms +- 3.7 ms -> 52.7 ms +- 3.1 ms: 1.35x faster
Significant (t=29.58)

### json_dumps ###
Mean +- std dev: 11.1 ms +- 0.1 ms -> 10.0 ms +- 0.1 ms: 1.11x faster
Significant (t=60.62)

### json_loads ###
Mean +- std dev: 21.9 us +- 0.2 us -> 20.8 us +- 0.3 us: 1.06x faster
Significant (t=24.16)

### logging_format ###
Mean +- std dev: 7.59 us +- 0.08 us -> 5.69 us +- 0.06 us: 1.33x faster
Significant (t=146.99)

### logging_silent ###
Mean +- std dev: 152 ns +- 4 ns -> 83 ns +- 0 ns: 1.82x faster
Significant (t=128.58)

### logging_simple ###
Mean +- std dev: 6.95 us +- 0.08 us -> 5.21 us +- 0.06 us: 1.33x faster
Significant (t=131.98)

### mako ###
Mean +- std dev: 13.0 ms +- 0.2 ms -> 8.9 ms +- 0.1 ms: 1.47x faster
Significant (t=154.67)

### meteor_contest ###
Mean +- std dev: 96.8 ms +- 0.3 ms -> 84.6 ms +- 0.3 ms: 1.14x faster
Significant (t=207.94)

### nbody ###
Mean +- std dev: 117 ms +- 1 ms -> 73 ms +- 1 ms: 1.62x faster
Significant (t=286.70)

### nqueens ###
Mean +- std dev: 82.3 ms +- 0.6 ms -> 68.2 ms +- 0.7 ms: 1.21x faster
Significant (t=122.65)

### pathlib ###
Mean +- std dev: 17.4 ms +- 0.1 ms -> 15.8 ms +- 0.2 ms: 1.10x faster
Significant (t=49.42)

### pickle ###
Mean +- std dev: 8.92 us +- 0.07 us -> 8.06 us +- 0.07 us: 1.11x faster
Significant (t=65.07)

### pickle_dict ###
Mean +- std dev: 23.5 us +- 0.2 us -> 24.3 us +- 0.1 us: 1.03x slower
Significant (t=-32.44)

### pickle_list ###
Mean +- std dev: 2.97 us +- 0.04 us -> 3.31 us +- 0.03 us: 1.11x slower
Significant (t=-56.69)

### pickle_pure_python ###
Mean +- std dev: 369 us +- 5 us -> 237 us +- 1 us: 1.55x faster
Significant (t=213.55)

### pidigits ###
Mean +- std dev: 157 ms +- 0 ms -> 156 ms +- 0 ms: 1.01x faster
Not significant

### pyflate ###
Mean +- std dev: 582 ms +- 9 ms -> 355 ms +- 7 ms: 1.64x faster
Significant (t=160.55)

### python_startup ###
Mean +- std dev: 8.06 ms +- 0.12 ms -> 7.32 ms +- 0.14 ms: 1.10x faster
Significant (t=57.03)

### python_startup_no_site ###
Mean +- std dev: 5.10 ms +- 0.04 ms -> 5.29 ms +- 0.05 ms: 1.04x slower
Significant (t=-40.36)

### raytrace ###
Mean +- std dev: 397 ms +- 5 ms -> 235 ms +- 1 ms: 1.69x faster
Significant (t=230.24)

### regex_compile ###
Mean +- std dev: 149 ms +- 1 ms -> 110 ms +- 0 ms: 1.35x faster
Significant (t=328.04)

### regex_dna ###
Mean +- std dev: 160 ms +- 0 ms -> 156 ms +- 1 ms: 1.02x faster
Significant (t=34.46)

### regex_effbot ###
Mean +- std dev: 2.59 ms +- 0.03 ms -> 2.64 ms +- 0.01 ms: 1.02x slower
Significant (t=-14.12)

### regex_v8 ###
Mean +- std dev: 19.6 ms +- 0.3 ms -> 16.9 ms +- 0.0 ms: 1.16x faster
Significant (t=61.11)

### richards ###
Mean +- std dev: 63.5 ms +- 0.7 ms -> 36.8 ms +- 0.6 ms: 1.73x faster
Significant (t=224.88)

### scimark_fft ###
Mean +- std dev: 329 ms +- 3 ms -> 239 ms +- 1 ms: 1.38x faster
Significant (t=216.68)

### scimark_lu ###
Mean +- std dev: 137 ms +- 3 ms -> 92 ms +- 2 ms: 1.49x faster
Significant (t=109.50)

### scimark_monte_carlo ###
Mean +- std dev: 90.2 ms +- 1.5 ms -> 51.6 ms +- 0.4 ms: 1.75x faster
Significant (t=197.44)

### scimark_sor ###
Mean +- std dev: 165 ms +- 2 ms -> 90 ms +- 1 ms: 1.84x faster
Significant (t=295.38)

### scimark_sparse_mat_mult ###
Mean +- std dev: 4.50 ms +- 0.17 ms -> 3.21 ms +- 0.02 ms: 1.40x faster
Significant (t=59.06)

### spectral_norm ###
Mean +- std dev: 128 ms +- 1 ms -> 79 ms +- 0 ms: 1.62x faster
Significant (t=295.70)

### sqlalchemy_declarative ###
Mean +- std dev: 144 ms +- 4 ms -> 118 ms +- 5 ms: 1.22x faster
Significant (t=32.39)

### sqlalchemy_imperative ###
Mean +- std dev: 17.6 ms +- 0.3 ms -> 14.5 ms +- 0.1 ms: 1.21x faster
Significant (t=66.06)

### sqlite_synth ###
Mean +- std dev: 2.32 us +- 0.04 us -> 1.85 us +- 0.03 us: 1.25x faster
Significant (t=75.74)

### sympy_expand ###
Mean +- std dev: 445 ms +- 2 ms -> 380 ms +- 2 ms: 1.17x faster
Significant (t=168.59)

### sympy_integrate ###
Mean +- std dev: 20.3 ms +- 0.2 ms -> 17.1 ms +- 0.1 ms: 1.19x faster
Significant (t=120.17)

### sympy_str ###
Mean +- std dev: 269 ms +- 2 ms -> 235 ms +- 2 ms: 1.14x faster
Significant (t=91.30)

### sympy_sum ###
Mean +- std dev: 155 ms +- 2 ms -> 137 ms +- 2 ms: 1.13x faster
Significant (t=62.20)

### telco ###
Mean +- std dev: 5.74 ms +- 0.11 ms -> 4.91 ms +- 0.17 ms: 1.17x faster
Significant (t=32.09)

### tornado_http ###
Mean +- std dev: 128 ms +- 5 ms -> 100 ms +- 3 ms: 1.27x faster
Significant (t=35.73)

### unpack_sequence ###
Mean +- std dev: 52.7 ns +- 0.7 ns -> 37.2 ns +- 0.4 ns: 1.42x faster
Significant (t=145.84)

### unpickle ###
Mean +- std dev: 10.9 us +- 0.1 us -> 10.1 us +- 0.1 us: 1.08x faster
Significant (t=61.64)

### unpickle_list ###
Mean +- std dev: 3.73 us +- 0.06 us -> 3.41 us +- 0.04 us: 1.09x faster
Significant (t=35.50)

### unpickle_pure_python ###
Mean +- std dev: 258 us +- 2 us -> 181 us +- 1 us: 1.42x faster
Significant (t=228.11)

### xml_etree_generate ###
Mean +- std dev: 80.3 ms +- 0.7 ms -> 64.3 ms +- 0.4 ms: 1.25x faster
Significant (t=156.04)

### xml_etree_iterparse ###
Mean +- std dev: 94.6 ms +- 1.0 ms -> 86.8 ms +- 1.7 ms: 1.09x faster
Significant (t=31.06)

### xml_etree_parse ###
Mean +- std dev: 141 ms +- 2 ms -> 134 ms +- 3 ms: 1.05x faster
Significant (t=12.88)

### xml_etree_process ###
Mean +- std dev: 64.4 ms +- 0.6 ms -> 45.1 ms +- 0.5 ms: 1.43x faster
Significant (t=187.55)

ワークロードによって異なりますが、おおむね、1.1~1.9倍の実行速度が得られました。

まとめ

Python3.11は、謳い文句通りPythonプログラムの実行の高速化が実現されていました。

実際のワークロードでは、フィボナッチ関数のような単純な関数の場合ほどではないものの、おおよそ1.2倍は高速化され、高速なインスタンス(CPU)への交換や、ソースコードの最適化をまったくせずに高速化できるのですから、お得としか言いようがありませんね。

参考