- CPythonプロジェクトは最近、バイトコードインタプリタに新しい実装戦略を導入した。初期結果では、さまざまなプラットフォームで平均10〜15%の性能向上が示された
- しかし、この性能向上は主にLLVM 19のリグレッションを回避した結果だった。より適切な基準(例: GCC、clang-18、特定のチューニングフラグ付きLLVM 19)と比較すると、性能向上は1〜5%に縮小した
性能結果
- 複数のコンパイラと構成オプションを使って、CPythonインタプリタの複数のビルドをベンチマークした。IntelサーバーとApple M1 Macbook Airでテストした。
- すべてのビルドでLTOとPGOを使用した。
clang18を基準として、pypeformance/pyperf compare_toで報告された平均を使用した。
- コンパイラ性能比較
- Apple M1 Macbook Air:
- clang18: 基準
- clang19: 1.12倍遅い
- clang19.taildup: 1.02倍遅い
- clang19.tc: 1.00倍遅い
- gcc: N/A
- 末尾呼び出しインタプリタは依然としてclang-18と比べて高速化を示したが、clang-19への移行に伴う速度低下の方がより顕著だった。
LLVMリグレッション
簡単な背景
- 従来のバイトコードインタプリタは、
whileループ内のswitch文で構成される。ほとんどのコンパイラはswitchをジャンプテーブルにコンパイルする。
- 現代のCコンパイラは、ラベルのアドレスを取得してそれを「computed goto」として使うパターンをサポートしている。CPythonは末尾呼び出しの作業以前までこのパターンを使っていた。
LLVM 19のリグレッション
- LLVM 19はtail-duplicationパスに制限を設け、IRサイズが特定の閾値を超えた場合に複製を打ち切るようにした。これによりCPythonではすべてのディスパッチジャンプが統合され、computed
gotoベース実装の目的が完全に失われた。
追加の異常
- 末尾呼び出し複製ロジックの変更がリグレッションを引き起こしたことは確信しているが、リグレッションの大きさを完全には説明できない。
- 現代のプロセッサでは、2〜4%の高速化の方がより一般的である。
computed gotoは必要か?
clang19.nocgベンチマークはclang19より速いと主張している。これはコンパイラがswitchベースのインタプリタを使って同じ最適化を行えることを示している。
修正
- LLVMプルリクエスト114990がこのリグレッションを修正した。この修正によって期待された性能が回復した。
振り返り
ベンチマークについて
- システムの最適化時には、ベンチマークとベンチマーク手法を構成し、提案された変更を評価する。
- ベンチマークは、特定のデータポイントを一般化するために、より多くの仮定と信念を必要とする。
ベースライン
- 新しい解決策や手法を提案する際には、「現在知られている最善のアプローチ」と比較するのが一般的である。
ソフトウェアエンジニアリングについて
- ソフトウェアシステムは複雑で相互接続されており、急速に変化している。
- 最適化コンパイラは、プログラマの意図を尊重しつつコードを最適化しなければならないという緊張関係の中にある。
最適化コンパイラ
musttail属性は、最適化に関わる新しい種類のコンパイラ機能を表している。これは性能に敏感なコードを書くための、より強力なスタイルを提供しうる。
nixについてもうひとつ
nixはこのプロジェクトで非常に有用だった。複数バージョンのPythonインタプリタの管理とビルドに大いに役立った。
1件のコメント
Hacker Newsのコメント
こんにちは。私はCPythonにtail-callingインタプリタを導入したPRの作成者です
ベンチマークは本当にうまくやるのが難しい作業です
著者がこの問題の真相を掘り下げたことに賛辞を送ります
Cが「機械に近い」言語ではないことを示す良い例です
コンパイラがループを構成する方法を調整することで、tail-callインタプリタは発表されたほど効果的ではなくなります
Pythonビルドの性能を評価するのは非常に難しいです
関連する議論:
素晴らしい記事です
最近、Python 3.9から3.13までのベンチマークを行いました
この種の最適化がtail-call最適化とどう関係しているのか気になります