- Python steering councilは、複数リリースにわたって GILをオプション化 するPEP 703を承認する意向を示しており、最終条件はまだ調整中
- CPython 3.13の
--disable-gil ビルドは実験用として用意され、stable ABI と拡張モジュールのホイール互換性が最大の技術的争点として浮上
- 既存の
abi3 ホイールは no-GIL の CPython 3.13 にはそのまま適合しない可能性があり、abi4 の導入、制限付きC APIの変更、参照カウントマクロの関数呼び出し化が議論されている
pip が GIL ビルド用と no-GIL ビルド用のホイールを誤って選ぶ懸念があり、静かに誤インストールされることは単なるインストール失敗より危険
- no-GIL ビルド名として
nogil ではなく free-threading が提案されたが、実行ファイル名・shebang・並行インストール・ディストリビューションのパッケージングまでまとめて解決する必要がある
PEP 703と no-GIL CPython の現在地
- Python steering councilは7月末、PEP 703 を承認する意向を明らかにした。このPEPは、CPythonで グローバルインタプリタロック(GIL) をオプションにする内容を含む
- 詳細な承認条件はまだ確定していないが、関連実装の議論とエコシステムの準備はすでに進行中
- 長期的には、GILのない単一のCPythonバージョンへ向かう道筋が想定されているが、当面は
--disable-gil オプションでビルドしたインタプリタで no-GIL 動作を試す段階
- CPython 3.13は2024年10月予定で、このバージョンの no-GIL ビルドは実験的な位置づけ
stable ABI と拡張モジュール互換性
- Sam Grossは、PEP 703とCPythonの stable ABI がどう関わるかをPython discussion forumで取り上げた
- stable ABI は、拡張モジュールを複数のCPythonバージョンで同じバイナリホイールとして動作させ、新しいCPythonリリースごとの再ビルドを不要にするための仕組み
- stable ABI 向けにビルドされた拡張は、CPython 3.13 no-GIL ビルドではそのまま動作しない可能性がある
- これを解決するため、制限付きC APIにいくつかの追加と変更が提案されている
- 制限付きC APIのみを使う拡張は、stable ABI を使うバイナリを作成できる
- 変更案には、オブジェクト参照カウントを増減させる一部マクロを 関数呼び出し に切り替える既存計画が含まれる
- 目標は、GIL ビルドと no-GIL ビルドの両方で動作する拡張バイナリを可能にすること
abi3、abi4、そしてホイール選択問題
- Victor Stinnerは、no-GIL 実験を成功させるには、両タイプのインタプリタで動作する拡張のための単純な解決策が必要だと見ている
- CPython 3.12以前で stable ABI 向けにビルドされた拡張は、3.13以降の no-GIL ビルドと互換性がないため、新しいABIバージョン abi4 を作る案が出ている
- 現在の stable ABI は
abi3
- ABI番号とCPythonメジャーバージョン番号が必ずしも連動するわけではない
- Grossは、no-GIL をサポートしたい拡張であれば、2つのバイナリホイールを作る負担はある程度受け入れられると見ている
- むしろ、C API と stable ABI の改善が no-GIL プロジェクトに過度に縛られる状況を懸念している
- Alex Gaynorも複数の
abi3 ホイールパッケージを持っているが、2つのホイールを一度作ることは過大な負担ではないと見ている
- ただし、既存および今後の
pip が適切なホイールを選べるかが重要
- Brett Cannonは、現行の
pip ロジックでは2つのバージョンを区別できないため、abi4 のような変更がなければ既存および古い pip は正しく動かないだろうと見ている
pip の静かな誤動作への懸念
- Grossは、CPython 3.13 の実験的な
--disable-gil ビルドにおいて、古い pip のサポートをそれほど大きく心配する必要はないと考えている
- 新しいPythonバージョンで古い
pip が壊れるのはよくあることだから
- 例として、
pip==23.1.1 以下が CPython 3.13 で pkgutil.ImpImporter 不在により壊れるケースを挙げた
pip メンテナのPaul Mooreは、明示的に壊れること と 静かに誤ったパッケージをインストールすること は別問題だと見ている
- 古い
pip を使うユーザーは存在する
- 明示的失敗と静かなエラーではユーザーへの影響が異なる
- Mooreは、no-GIL または free-threaded ビルドを試そうとするユーザーが ABI 互換性問題のデバッグを強いられるなら、意欲を失う恐れがあると懸念
- Gaynorも、影響を受けるパッケージで
pip が静かに誤動作すると、issueが殺到しかねないと見ている
並行インストールと実行ファイル名
- Barry Warsawは、同じシステムに GIL ビルドと no-GIL ビルドを共存インストールする計画があるのか質問した
- Grossは、これは異なるPythonバージョンをインストールするのと同じ状況だと答えた
- Cannonは、1つの「fat」wheelに2つのバイナリを入れる案も可能だと見ている
- ただし、ホイール内のバイナリ名は互いに異なる必要がある
- 実行ファイル名の議論は別スレッドへと続いた
- Paul Mooreは、ユーザーが no-GIL モードを簡単に試せて、GIL/no-GIL のどちらかを簡単に選べる必要があると考えている
- Windows、macOS、Linux などでこの過程が難しければ、no-GIL プロジェクトに悪影響を及ぼしかねない
- ユーザーが簡単に試せてこそ no-GIL ビルドへの需要が生まれ、パッケージメンテナにも no-GIL 互換ホイール提供の圧力がかかる
nogil と free-threading の名称論争
- Barry Scottは、shebang行でどのインタプリタを呼ぶかを示す必要があるため、実行ファイル名が重要だと見ている
- 例として
python-nogil3、python-nogil3.13 のような名前を挙げた
- Gregory P. Smithは、CPython 3.13 の no-GIL ビルドは実験機能なのだから、ディストリビューションがデフォルトの
$PATH に置くべきではないという個人的見解を示した
- 長い実行ファイル名が shebang に残って長期化する状況も望ましくないと考えている
- インストール名の決定を3.14以降へ先送りする案を提案した
- Fedora開発者Petr Viktorinは、ディストリビューションがユーザー実験のために no-GIL インタプリタをパッケージ化しようとする可能性が高いと指摘
- Mooreは、
#!/usr/bin/env python3.13-nogil のような形で free-threaded ビルドを指定したいと考えている
- 長く直感的でないパスをハードコードしないための要件
- Steve Dowerが始めたWindowsインストーラ関連スレッドで、Smithは steering council が
nogil という名称を避けたがっていると明らかにした
- 理由は、多くの non-core 開発者にうまく伝わらず、GIL が何かを知る必要もなく、否定形の表現を含むため
- 代案として free-threading という用語が提案された
- Grossは、
free-threading も部外者に理解しやすいわけではなく、広く使われている用語でもないと見ている
- 実際の議論では短い名前への支持が強く、その点では
nogil が最有力候補だった
- 具体的に反映された変更は、no-GIL ビルドのABIタグを
n から t に変えたこと
abi4 提案と残る作業
- GrossとViktorinはAPI変更案の問題点を議論し、そのフィードバックが新しいABIである abi4 の提案につながった
- Grossは新しいABIの プロトタイプ を作成した
- Viktorinは全体的なアプローチには同意するものの、詳細はさらに整理が必要だと見ている
- Stinnerは abi4 に関するPEPが必要だと考え、Viktorinはこれを pre-PEP 議論として受け止めている
- 制限付きC APIバージョンと
abi3 の組み合わせが提供する互換性保証には混乱があり、この点は abi4 の方向性にも影響する
- 関連調査は継続中で、10月中旬の core developer sprint で対面議論が行われる可能性がある
最終承認文言と長期的影響
- no-GIL または free-threaded CPython の作業は続いているが、PEP 703 の最終承認はまだ保留中
- 遅延はやや長引いたものの、PEP 703 とその波及効果は今後5年以上にわたりCPython開発とエコシステムに大きな影響を与える可能性がある
- steering council は承認基準を明確化しようとしている
- Thomas Woutersは、正確な承認文言を詰めつつ複数の判断事項を明確にしようとしていると述べた
- 一部作業は core developer sprint でも進められる可能性がある
1件のコメント
Hacker News の意見
現代のコンピュータを見ると、明示的な並列性は、教科書で流行している以上にコンピュータサイエンスのより根本的な要素になるかもしれないと思う
もはや常に明示的に並列コードを書くべき段階なのかもしれない
例えば
forループはforeach、map、filterのような操作に置き換えられつつある。こうした表現は、データ構造の全要素に何らかの処理を適用したいという意図をコンパイラ/インタプリタに伝え、並列化するかどうか、またその方法はコンパイラ/ランタイムに任せるWebサービスの実行では、個々のリクエストは十分に速く、並列性の本当の利点は多くのリクエストを並行して処理することにある。ここに No-GIL がうまく当てはまる
単一リクエスト内にサブリクエストが多い場合は通常非同期コードで処理するが、これは非同期の性能上の利点というより、スレッド生成が高価だったりスレッドプールが面倒だったりするためであることが多い。非同期はスループットには良いがレイテンシには悪く、サービスリクエストを並列化するなら通常はレイテンシをより気にする。非同期は主に使いやすさのために勝った
もう一つの並列性は、大規模なオフライン処理に見られる。MapReduce や Presto のようなもので、概して分割統治問題のような形をしている。GPUでのモデル学習もこれに近い
起きなかったのは、ローカルで高度に並列なアルゴリズムだ。Webサービスではデータサイズが小さくレイテンシ上の利得が小さいうえ、実装が複雑で、スレッド間の調整コストが大きくなる。小さな例外はベクトル化アルゴリズムだが、これは1コア上で実行されるため調整オーバーヘッドがなく、オンライン推論もまた非常に強くベクトル化されている
時間が経つにつれて、どちらも良くなっている。より多くの言語やライブラリがデフォルトで安全になっているのと同じように、今ではより多くのものがデフォルトで並列的になっている。まだ先は長いが、あまり早くやらなかったのは幸いだったと思う。この10年で技術が大きく向上したからだ
例えば Rust の Rayon で安全にできることと、C++ の OpenMP で安全でない形でやっていたことを比較できる
さらに外側には、私が取り組んでいるこうしたものもある: https://legion.stanford.edu/, https://regent-lang.org/, https://github.com/nv-legate/cunumeric
実装の詳細なので、より簡単に活用できるよう抽象化できるなら、そうすべきだ
比較するとミューテックスは約25ナノ秒で、競合があればさらに増えるが、ミューテックスはポイントツーポイントの同期である
Disruptor の良い点は、複数のスレッドが同じメッセージを大きな追加作業なしに受け取れることだ
https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
https://gist.github.com/rmacy/2879257
Smalltalk に似ているが、並列化に意味が出るまではシングルスレッドのままでいられる言語を夢見ている
ビッグデータではない並列性の問題を探している。並列性は車の速度を上げるというより、道路にもっと車を入れることに近い。だがデスクトップやモバイルのユーザーが、ローカルでコンピュータの数学的な力を活用して何をすべきなのかは、まだ探しているところだ
並列性のアイデアとして Itanium や VLIW アーキテクチャも考えている
-ngを使えばよい。no-gil または next-generation という意味で新しいコンパイラフラグ、新しいリンカフラグ、別のライブラリのリンク、まったく別のコンパイラコマンドの使用まであった。AIX は特にそうだった
シバン問題は、既存の Python の慣例に頼るのがよさそうだ:
from __future__ import nogilその時点でインタプリタをホットスワップすればよい
from __future__ importはランタイム文ではなく、フラグを表す特殊な文であるhttps://docs.python.org/3/reference/simple_stmts.html#future...
future 文はモジュール単位であり、GIL/no-GIL はそのモデルに簡単には合わない
この提案を見るたびに、プログラムが依然として正しく動作することをどう保証するのか気になる。既存のマルチスレッド Python コードのかなり多くは、安全でない書き方をされている
特に、複数の企業のコードベースやオープンソースプロジェクトで繰り返し見てきた データ競合 が問題である。こうしたプログラムは、GIL が一度に1つのスレッドにしか実行を許さないことに暗黙に依存しているため、壊れずに済んでいるだけである
GIL がなくなれば、こうしたプログラムは壊れるだろう。Python は動的型付け言語なので、既存の Python プログラムからこの種の問題を見つけ出せる静的解析器が存在するかは非常に疑わしい
より起こりそうなのは、実行時に非決定的に現れる巧妙なバグである。いっそクラッシュしてくれればよいのだが、この種のバグは誤った動作をする結果につながる可能性が高い
もしかすると、この GIL なしの提案は大半のプログラムで使うためのものではないのかもしれない。プログラマが GIL がないことを理解し、それに合わせて書けるごく一部の状況のための、極めて特化したツールなのかもしれない
GIL は、一度に1つのスレッドだけが Python バイトコードを実行できるという意味にすぎない。GIL のあるインタプリタでも、バイトコードの間でスレッドを切り替えることができ、多くの Python 演算は複数のバイトコードを必要とする。多くの人が「アトミック」だと思っている組み込み型の組み込みメソッドも含まれる
だから Python は、現在 GIL があるにもかかわらず、ロック、ミューテックス、セマフォのようなものを提供している
GIL をめぐって競合するスレッドは、すでに悪いタイミングで互いに GIL を奪い合い、混乱を生み出すことがある
すべての依存関係が許可した場合にだけプログラムが GIL なしで実行されるなら、そうしたバグを直す時間は十分にあるはずだ
そうすると、大規模にこの問題を扱うことになるのは、おそらく2030年が近づく頃だろう。今使っているランタイムを最新リリースへすぐに上げるプロダクションもあまり見かけない
厳しく聞こえたいわけではないが、Steering Council は 2 から 3 への移行のようなものをもう一度は望んでいないと言っているので、人々が気軽にアップデートすることはないだろう。今オンラインにある内容の大半は、コピー&ペーストするには危険かもしれない
実際の Python コードにはスレッド関連のバグが非常に多い
OCaml も似たような進化を遂げたのではなかったか。2つのプロジェクトの間に比較できる点があるのか気になる
そのため既存のスレッド API は現在の domain 内でスレッドを作成し、ロックを取得すると期待しているコードを隔離できる。新しいコードは代わりに、1つのスレッドで始まる新しい domain を作れる。両者を意図的に一緒に使って、スケジューリングの形として利用することもできる
Python は、ライブラリ作者の制御の外で、ロックをグローバルに完全に任意のものにしようとしている。ただし Python のロックはランタイム自体だけを保護することが保証されているように見えるので、そのロックに依存するコードの大半はどのみちバグを抱えている可能性が高く、そのため Python の計画も実行可能に見える
共通点があるとすれば、ランタイム全体のコードベースで予期しない共有状態を見つけて修正し、C ABI を改訂しなければならないという程度だろう
これで Python もマルチスレッド性能で Tcl に追いつく機会ができた: https://www.hammerdb.com/blog/uncategorized/why-tcl-is-700-f...
むしろ Python コードを Mojo にポーティングして、マルチスレッディング、SIMD、その他の高速化を得たい
Python コードを nogil Python に移したくないって? ならダウンボート、という感じだ
名前を付けるなら
python4、python3-gilfoil、python3-gilfreeなどがあり得る現在、GIL なしの Python に注力している流れはかなり奇妙に感じる。Faster CPython チームは、リリースごとに CPython の性能を 50% 高めるという野心的な目標を掲げていた。
3.11 では実際に改善があったが、50% にはほど遠く、私たちのテストのかなりの数では 3.12 は同程度か、むしろ遅かった。本物のマルチスレッドは素晴らしいだろうが、まずは シングルスレッド性能 が改善されることをずっと強く望んでいる。
もちろん、私たちのニーズがすべての人を代表しているわけではないかもしれないことは認めるし、Python を素晴らしい言語にするために費やされたすべての作業には感謝している。それでも、自分が何を見落としているのか気になる。
現状、複数コアの利用は multiprocessing を通じて行われるが、制約が多い。複数インタプリタがコルーチンのようなものと一緒に来る可能性があるのは理解しているが、それでも本物のマルチスレッドという選択肢のほうが好ましい。
nogil Python では、たとえば Python オブジェクトとしてアクセス可能な共有状態を持ちながら、複数のスレッドが C コードを呼び出せる。これは 機械学習 ではかなり重要で、実際に今回の PEP の現在の形は PyTorch チームから出てきたものだ。
シングルスレッド性能も重要だが、重要な箇所については、すでに numba、Cython、そして Mojo のようなかなり良い回避策があった。
順序も重要だ。nogil が導入されると、faster CPython の作業のかなりの部分が完全に捨てられる可能性があるため、チーム間で調整する必要があった。
理想的な世界なら、nogil モードとシングルスレッド性能改善の両方がある形だ。Guido も高度な JIT を検討中だと示唆していた。
Python は、低レベルの抽象化をより高水準の言語から扱うことを非常に楽にしてくれる。だから長年の Python 開発者として、GIL についてはそれほど大きなストレスを感じてこなかった。
どちらか一方だけを選ばなければならないなら、ほとんどのユースケースには単により速いシングルスレッドコードのほうが合っている、という点には同意する。ただし、両方を持ってはいけない理由もない。
hindsight では明らかだが、Python 側が 2 から 3 への移行がどれほど長く苦痛を伴うものになるかを知っていたなら、インタプリタ内部もはるかに大きく手を入れていたのではないかと思う。
12 年がかりの移行を経たにもかかわらず、シングルスレッド性能 はいまだにひどく、本物のマルチスレッドに到達するには、まだ何度か苦痛を伴う移行が残っている。
オープンソース開発には寛容であるべきだとは分かっているが、どの時点から「非常に管理のまずい言語」と呼んでもよいのか疑問に思う。
Python の最悪の部分は、Python があまりにも人気でエコシステムがあまりにも大きいために変更が難しい部分だ。だから、あらゆる種類の変更が後方互換性のためにより難しくなる。
Python をあまりにもすぐ擁護しがちな傾向がある。偏りなく客観的に見ることが重要だ。
性能と Python 構文を求めるプロジェクトはそちらへ行けばよい。現在の Python は複数の目標の間でもがき、どれもきちんと達成できていないように見える。
Perl 5/6 が例として挙げられていた。誰も移行しないことが明らかになった後でさえ、それをより簡単にしようとする試みまでにさらに約 5 年かかった。