Python 3.14のテールコールインタープリターのパフォーマンスは本当によかったのか?
引用元:https://news.ycombinator.com/item?id=43317592
こんにちは!私はCPythonにおけるテールコールインタープリタのPRの作者です。まず最初に、これを解決するために約1ヶ月をかけたNelsonに感謝したいです。それから、私が大きな見落としをしてしまったことに対して、非常に恥ずかしく思っており謝りたいです。私もCPythonチームも、ベースラインで使っていたコンパイラがそのバグを持っているとは予想していませんでした。謝罪のブログを投稿しました。
あなたがそんなエラーバグを見落としたことに恥じ入っていると聞いて、CPythonのパフォーマンスを悪化させたのかと想像していました。ですが、全然違いましたね。10~15%のパフォーマンス向上を発表しましたが、それは実際にはバグのないコンパイラでの1~5%の向上に過ぎなかった。あなたは勉強して改善し、PRも他の人にレビューされているのに、その数値が誤解を招くようなものだったのは不運です。誰もが合理的に犯す可能性のある間違いのように思えます。
そのバグを間接的に修正することで、間違ったコンパイラを使っていた全員に約10%のパフォーマンス向上をもたらしました!それは多くの人が知らないようなオプションフラグよりも素晴らしいことです。
そのパフォーマンスの退化は、同じ計算済みのgotosを持つ非常に多くのパスを使用しているコードにのみ影響しました。それは比較的複雑なインタープリタだけが影響を受ける可能性が高いです。ですから、広範囲にわたるパフォーマンスの向上というわけではありません。しかし、コンパイラの新しいヒューリスティックが失敗した例を持つのは興味深いです。
今、確かにその影響を受けているのは、そのコンパイラでビルドされたPythonを使っている全員を含んでいます。
あなたは宿題をやったと思う。改善を行い、10~15%のパフォーマンスの向上を測定し、PRは他の人にもレビューされている。しかし、その10~15%の数値が誤解を招くのは、あなたが使ったclangのバージョンに問題があったからです。間違いがあったとしても、不幸な間違いのようです。
確かにそうかもしれません!面白いですね、Gettier問題を知らなかったので教えてくれてありがとう。そのページをHNの投稿として提出してみてはどうですか?
ちなみに、あなたがそのブログを投稿した後、修正はマージされましたよ。加えて、古いPythonインタープリタにとって3~5%の向上は大きなことです。非常に誇らしく思いますよ。30年も経つと、性能改善に懐疑的になることを学びました。明らかに得られることもあるけど、そう頻繁ではありません。
30年も経てば、長い間パフォーマンスが重視されるシステムにおける重要なパフォーマンス向上には懐疑的になる。
もちろん、私も長い間パフォーマンスが重要だったシステムへの大きな改善には懐疑的です。
0.5%の改善なら信じるし、10000%の改善も信じるけど、10%は怪しいな。
テイルコールインタープリタのデザインの主な目的は、オプティマイザの影響を受けにくくするためだよ。具体的には、オプティマイザが重要な変数をレジスタに保持させないとか、同じコードパスを合併させちゃって最悪の性能になることがあるんだ。それに比べてテイルコールインタープリタは、欲しい機械コードのパターンをインタープリタ内で表現できるから、オプティマイザに頼る必要が減るんだ。だから、パフォーマンスの改善は3-5%以上にもなると思う。
これは良いポイントだね。LTOとPGOを使ったビルドでこれを観察したよ。最近のコンパイラだと各LTO+PGOビルドが1-2%もパフォーマンスに変動があるんだ。テイルコールインタープリタがあればこの影響を受けないかもしれないね。
その通りだと思う。さまざまなコンパイラ構成に対してパフォーマンスが安定しているというのは大きな価値があるよ。
自分のミスを素直に謝れるのは素晴らしいと思う。今の世の中、そういうことが難しいからね。
フラストレーションは理解するけど、そういう言い方は必要なかった気がする。ニュースが世の中の人々の見方に影響を与えないようにしたいよね。
分断と征服が進んでる。私たちはお互いを憎んで、国家やエリート、技術を信じることが予定されてるんだ。真の犯罪者はその計画の設計者たちだよ。
このベースラインパフォーマンスの劣化が、速いCPythonのベンチマークページに出なかったのはどうしてだろう?同じような問題を今後防ぐためにベンチマークを改善できるかも。
普通よりは良いベンチマークページだね。ただ、IntelとARMのCPUだけでテストしたのが気になる。もっと多様なCPUが必要だと思う。
最近のコンパイラを使ったベンチマークは過去のデータと比べられなくなるから通常はやらないんだよね。Clang 19は昨年リリースされたけど、ベンチマークは数ヶ月前にやったもので、macOSでの明らかな遅延を確認したよ。でもこれはXcode Clangに対するもので、違うコンパイラだから深く調べなかった。今思えば後悔してるけど。結局Clang 19と18の比較ベンチマークはしてなくて、他のコンパイラとの比較のみだったから、コンパイラの違いだと考えていたよ。
それは良い質問だね。ブログの記事によると1~5%のパフォーマンス改善があるみたいで、CPythonには大きいと思う。ソースも複雑にはならないし、自動生成されたコードなので、メンテナンスのためには今後もこの改善を維持すべきだと思う。バイトコードが小さい関数であれば、デバッグも楽だし。パフォーマンスと生産性の面で価値があると思うよ。 壊れやすいコンパイラの最適化に対して強固になるのも重要な利点だよ。インタープリタのループは特殊なコードで、コンパイラのヒューリスティックに任せるには気を付けるべきだと思う。望ましい呼び出し構造がポータブルに達成できるなら、それは勝利だね。 2022年に全体で5倍のスピードアップを目指す計画があったんだ。その後GuidoとチームがCPythonを大幅に速くする発表をしたんだけど、実際に2倍のスピードアップは見られたのか?それともせいぜい0.2倍とか?インタープリタの変更を否定するつもりはないけど、そのスピードアップの計画が実現可能だったのか、1/5も達成できるのか知りたい。 Faster CPythonプロジェクトは3.10を基準にして5倍を目指してるんだ。CPython 3.13は3.10よりも1.6倍速くなって、JITを有効にするとさらに若干速くなる。3.14の変更で1.8倍のスピードアップになるのが予想されてる。進展はあると思うけど、JITの最適化が進むことでまだもっと速くなる余地があると思うよ。 そのプロジェクトから多くのスピードアップが実現されてきたよ。一度にすべてが行われるわけじゃなく、過去のリリースに分散しているから、個々のスピードアップは地味に見えるかもしれないけど、累積しているのを忘れないで。 ここでの詳細はここにある:https://github.com/markshannon/faster-cpython/blob/master/pl...著者について:https://us.pycon.org/2023/speaker/profile/81/index.html >彼の学術的および商業的な仕事は、Pythonのためのコンパイラ、仮想マシン、静的解析に焦点を当てている。彼の博士号は動的言語のための仮想マシンの構築に関するものだよ。わけて言うと、彼は神レベルだね。冗談半分だけど、MSFTもGoogle V8のLars Bakをスカウトできるかも。 Pythonの発展を推し進めるために素晴らしい仕事をしてるよ。みんなの努力に感謝してる! この問題の対処が非常に良い形で行われたね!間違いを修正するためにここまで気を使ってくれて感謝してる。Pythonの開発者たち全員に感謝し、尊敬しているよ! Pythonを改善するための努力に感謝。特に、明確で目立つ形で記録を正す手伝いをしてくれてありがとう。一部のコメントは「謝る必要はない」と言ってるけど、これは「高い基準を求めなくていい」って読み取れる。つまり、僕は反対だ。責任感や知的誠実さが軽視されている現在、結果を報告する前にもっと調査すべきだったと言えることは、高い基準を持つということだと思う。もう一つの見方は「気にする必要はない」というもので、これは賛成。多くの場合、自分の仕事を十分に挑戦できていないことがある。これは誰にでもあることだよね。 >「責任感や知的誠実さが軽視されている」 うん、あまり気にしないで。君の失敗が公開されただけで運が悪かっただけだよ。皆、プライベートで同じような失敗やってるし。Pythonの最適化について知ってる人なら、数パーセントのスピード改善でも驚くよ。それは多くのGWhの電力を節約することにもつながるし、数パーセントでもすごいことだよ!君は新しいアカウントで、ブログもまだ一つの記事だけみたいだから新しい開発者かな?でも、すごくいい仕事してるから頑張って! Pythonをより良くするための仕事をしてくれてありがとう。 5%の変更があったとしても、すごく素晴らしい仕事として見られると思うよ。誠実な間違いに対処する姿勢には感謝してるし、Pythonコミュニティへの努力も敬意を表するよ。 恥じたり謝ったりする必要はないよ。パフォーマンスが向上したんだから。短期的には知らなかったバグを回避したし、長期的にはバグが修正されても向上はある。ソフトウェア開発は抽象に依存するしかないし、すべてを正確に知るのは難しい。スマートな人々は何かを作り、他のスマートな人々は問題を見つける。何も壊れてないよ。 謝る必要なんてないよ。どんな結果でも君は素晴らしい仕事をしたんだから。 依然としてパフォーマンス改善があったし、Pythonの規模で考えると全球的な電力節約につながると思うよ! 長い心からの謝罪は不要だ。一言「くそっ」が十分だ。間違いはない、ただ運が悪かっただけだ。もっと慎重になることで運を改善できるけど、過度に気を使うことで試すこともできなくなるから、君のスタイルを続けてほしいな。 この見落としを気にする人はいない。もっと重要なのは、君が僕たちのために開発に時間を使ってくれることだ。ありがとう。 ベンチマークってホントやるの難しいよね。多くの要因が誤解を生むことがあるし。最近、アルゴリズムを15%速くする方法を見つけたんだけど、実行してない方が速くなるってどういうこと!?要するに、コードやメモリ配置の問題で、CPUキャッシュとの相性が良くなったかな。スピードアップが本当にコードの改善によるのか、いい偶然だったのか分からないし。Casey Muratoriのシリーズがこの辺りをよく説明してるよ。 そのリンカーの運が15%も改善したのは驚きだね。どういうケースでそんな大きな改善が出るんだろ?それが珍しいのか、どうやって気づいたのか教えて。 いろんな研究が、この手の誤差が15%以上になることを示してるよ。そんなに珍しくないし、結構遭遇する。コンパイラーやリンカーは頑張ってるけど、現代のCPU自体がすごく複雑だからね。Casey MuratoriのシリーズがCPUレベルでのことを分かりやすく解説してくれている。 追加の背景だけど、特定の探索アルゴリズムのためにベンチマークツールを書いてたんだ。ノイズを減らすためにかなり手間かけたし、CPUピンニングや何度もテストして、同じデータで一番良いスコアを取るようにした。でも、ツール自体は再現性があったんだけど、どのアルゴリズムが良いかの正確さは信頼できなかったから、結局そのプロジェクトはあきらめたよ。 コンパイラーの決定を意図的にランダム化して、コードの本当のパフォーマンスを安定させるためのベンチマークプロジェクトのことをぼんやり覚えてる。 それはEmery Bergerの「Performance Matters」っていうStrange Loopsのトークのことだと思うよ。https://youtube.com/watch?v=r-TLSBdHe1A 類似のことをしてたStabilizerってのがあったけど、今はメンテされてなくて、最新のLLVMには対応してない。もっと新しいやつが出てるはずだけど、名前は忘れた。 Emery BergerのCozプロファイラーについても。これなら、どの関数を変えれば望むレイテンシーやスループットが得られるか、良い推定ができる。 LLDにはこれを目的とした新しいオプション「–randomize-section-padding」があるよ。https://github.com/llvm/llvm-project/pull/117653 面白いね、ありがとう! なんか明らかにおかしいことしてないのに、間違ったデータが出てきた! なんか明らかにおかしいことしてないのに、間違ったデータが出てきた! これは多分Emery Bergerのプロジェクトで、プログラムの異なる部分を意図的に遅くして、どの部分がパフォーマンスに影響が大きいかを探ってるんだろうね。 Aleksey Shipilёvっていう長年のJavaのパフォーマンスエンジニアがベンチマークの大変さについてたくさん書いてるから、ブログやトークをぜひ見てみて! 著者にはほんとに感謝!今回の話はPython 3.14のテールコールインタプリタの改善についての貴重な教訓を示してくれたね。パフォーマンスの主張を全て再考させるきっかけになったし、他にも検証も必要だな。 10%のパフォーマンス低下がどうして見つからなかったのか大きな疑問だな。コンパイラ自体のベンチマークはやってないの? LLVMがCPythonで大きなパフォーマンスの低下を引き起こして、なんで誰も気づかなかったのかが驚きだね。 公式のCPythonバイナリはGCCで作ってるから、Clang 18や19でビルドしないとスピードの違いに気づかないかも。だから誰も気付かなかったのかも。 Cが機械に近いとかポータブルアセンブリじゃないってすごくわかる例だね。意図した最適化とは逆の結果が出ることがあるんだよね。 ”Cはポータブルアセンブリ”というのは、基本的に他のシステムプログラミング言語から見るとそうなんだよ。Cの表現である’a += 1’は数値を正しくインクリメントするけど、C++だとメモリを割り当てたりスタックが巻き戻ったりするかもしれない。”Cはポータブルアセンブリ”って言葉は、各文が必ずしも同じ機械コードに変換されるという意味ではないよ。 コードがclangやgccのIRに来ると、その時点で’a’は存在しないんだよ。SSA形式は変化しないから、新しい変数に割り当てられるんだ。1のインクリメントがあるかどうかもわからないし、加算がまとめられてしまうかもしれないし、場合によっては16のチャンクで処理されて、最後のチャンクで調整が必要ってこともあるんだ。 高レベルの学術的な見解では、コンパイラは合法的な変換を行うことができるんだけど、実際のCコンパイラは出力する内容について結構保守的なんだ。-march=なしでコンパイルした場合ね。中程度に複雑なオープンソースのCライブラリを見つけて、それをコンパイルしてHexraysやGhidraやradare2でその結果を見れば、元のソースと比較してあんまりマジックはないってわかると思うよ。 -O3はオートベクタライゼーションを行うんだ。ループをSIMD命令に変換したりして、時にはパフォーマンスプロファイルが大きく変わったりもするんだよ。オートベクタライゼーションが”あんまりマジックじゃない”なら、他に何がマジックなんだろうね。 ”Cはポータブルアセンブリ”って言葉は、各文が必ずしも同じ機械コードに変換されるという意味ではないんだよ。ウィーゼルワードってやつだね。自動運転車の話をしてるみたいで、運転手が常に注意を払ってなきゃいけないみたいなものさ。Cを勧める人は、特定の機械的な結果を達成できると思っている様子があるけど、そんな時もあればそうでない時もある。 一般的に、技術的な議論をしているときに、すべての参加者が特定の表現を使っていて、あなたが理解できない時は、少し確認するべきだよ。例えば、Cが”ポータブルアセンブリ”かどうかの話の中で、アセンブリプログラミング時代を思い出してしまうかもしれない。Cはマクロや制限された環境で書くことができるけど、実はそこ、本当に話題になっていることではないんだよ。 ”Cの’a += 1’は数値を確実にインクリメントする”って言ってるけど、未定義の動作を知らないの? もしこのコードを誤ってコンパイルするCコンパイラがいるなら、僕のポイントを認めるよ:uint32_t add_1(uint32_t a) { a += 1; return a; }。 Cは他のシステム言語から見ると低レベルだけど、現代の宇宙船に対してApollo 11がシンプルとは言えないようなものだよ。Cのコードは実行されるものとはあんまり近くない。例えば、たいていのコンパイラは以下のコードの’a’をインクリメントしない可能性が高いんだ。uint32_t add_and_subtract_1(uint32_t a) { a += 1; a -= 1; return a; }。 もしコンパイラに自分のコードをリテラルなポータブルアセンブリとして扱わせたければ、最適化をオフにすることだよ。 なんで同じ変数から1を減らしたら、1を増やすの?無駄なサイクルじゃない?って思うよ。良いコンパイラならこれを最適化できると思うし、何か誤解があるかな? たぶんあれはCがアセンブリに近いってコメに対する直接の返信だと思う。便利かどうかは関係ないけど、動作が正確じゃないってことだね。 Cが「ポータブルアセンブリ」って言われるのは、単に個々のソース式から決定論的なマシンコードが生成されるわけじゃないってことを強調したいんだ。他の言語の例もあるし、コンパイラの出力はもっと複雑なことがあるよ。 こちらは、全角リンクを貼っとくね:<https://godbolt.org/z/r39jK1ddv>。-O0だと、まずインクリメントしてからデクリメントするよ。だから特に問題はないと思うんだ。 C標準は変わらない動作を保証してるんだ。それが標準の目的だよ。ただし、特定のアセンブリ命令が使用されることを保証はしていないね。 それはちょっと分かりにくい例だけど、重要なプログラムではその間にコードがあったり間接的な参照があることが多いんだ。だからコンパイルされたコードを見ても、ソースコードと直接の対応があるとは限らない。コンパイラの仕組みを理解して、ハードウェアやアプリケーションの知識も必要なんだ。 コンパイラがインクリメントは当然やるだろうけど、デクリメントはしないし、古い値を使うのが普通だよね。 Cの例を一つ挙げただけじゃ全然弱いよ。もっと興味深い状況を提示しないと。C++でもこのテストは通る。 ’a += 1’に未定義動作があると示したけど、CやC++に問題があるのは承知さ。で、インターネットにはたくさんの無名整数足し算の例があるよ。 ’int a’で’a += 1’が驚く結果をもたらすことは分かってる。C++でも同じように信頼できそう。 Cは演算子オーバーロードが無いから、 CやC++の問題は山ほどあるけど、最適化がセキュリティチェックを省略しちゃうことには困るよね。 だから『a += 1』が数値を1増やすと理解しやすいってのは間違いで、元のソースと機械語のマッピングが難しいってことだよ。別の例もあるから見てみて。もっとコメントを表示(1)
HNでのこのようなネガティブな発言に疲れた。僕の人生では、常に人々が「責任感や知的誠実さが軽視されている」と文句を言っている。地方ニュースのインタビューと同じレベルだ。「最近の交通はどう?」。「悪化している」。いつの時代に責任感や知的誠実さが「正しく」評価されていたんだい?もっとコメントを表示(2)
もっとコメントを表示(3)
a += 1
ってのは数値を1増やすって理解しやすいんだ。C++だとそれが難しくなる。