1 ポイント 投稿者 GN⁺ 2023-10-22 | 1件のコメント | WhatsAppで共有
  • 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 ビルドの両方で動作する拡張バイナリを可能にすること

abi3abi4、そしてホイール選択問題

  • 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 互換ホイール提供の圧力がかかる

nogilfree-threading の名称論争

  • Barry Scottは、shebang行でどのインタプリタを呼ぶかを示す必要があるため、実行ファイル名が重要だと見ている
    • 例として python-nogil3python-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 に変えたこと
    • t は threading を意味する

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件のコメント

 
GN⁺ 2023-10-22
Hacker News の意見
  • 現代のコンピュータを見ると、明示的な並列性は、教科書で流行している以上にコンピュータサイエンスのより根本的な要素になるかもしれないと思う
    もはや常に明示的に並列コードを書くべき段階なのかもしれない

    • 人間は複数のスレッドを同時に推論するのが苦手なので、より実用的な変化は、すでに見えている宣言型構文の方向である可能性が高い
      例えば for ループは foreachmapfilter のような操作に置き換えられつつある。こうした表現は、データ構造の全要素に何らかの処理を適用したいという意図をコンパイラ/インタプリタに伝え、並列化するかどうか、またその方法はコンパイラ/ランタイムに任せる
    • 並列性はいくつかの方向に分かれている
      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
    • 並列性はメモリ管理と同じ種類のものだと見ている。私たちが書くほとんどのプログラムは、何らかの形で自動管理を使えるし、使うべきであり、手動管理は性能上必要な領域に残せばよい
      実装の詳細なので、より簡単に活用できるよう抽象化できるなら、そうすべきだ
    • LMAX Disruptor の wiki には、あるスレッドから別のスレッドへメッセージを送る平均レイテンシが 53ナノ秒 とある
      比較するとミューテックスは約25ナノ秒で、競合があればさらに増えるが、ミューテックスはポイントツーポイントの同期である
      Disruptor の良い点は、複数のスレッドが同じメッセージを大きな追加作業なしに受け取れることだ
      https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
      https://gist.github.com/rmacy/2879257
      Smalltalk に似ているが、並列化に意味が出るまではシングルスレッドのままでいられる言語を夢見ている
      ビッグデータではない並列性の問題を探している。並列性は車の速度を上げるというより、道路にもっと車を入れることに近い。だがデスクトップやモバイルのユーザーが、ローカルでコンピュータの数学的な力を活用して何をすべきなのかは、まだ探しているところだ
      並列性のアイデアとして Itanium や VLIW アーキテクチャも考えている
  • -ng を使えばよい。no-gil または next-generation という意味で

    • Unix のスレッド対応の大きな波を思い出す。開発者がしなければならないことはプラットフォームごとにものすごく違っていた
      新しいコンパイラフラグ、新しいリンカフラグ、別のライブラリのリンク、まったく別のコンパイラコマンドの使用まであった。AIX は特にそうだった
  • シバン問題は、既存の Python の慣例に頼るのがよさそうだ: from __future__ import nogil
    その時点でインタプリタをホットスワップすればよい

    • from __future__ import はランタイム文ではなく、フラグを表す特殊な文である
      https://docs.python.org/3/reference/simple_stmts.html#future...
    • 「future 文は、特定のモジュールを、その機能が標準になる将来の Python リリースで使われる構文や意味論でコンパイルするよう指示するコンパイラディレクティブ」である
      future 文はモジュール単位であり、GIL/no-GIL はそのモデルに簡単には合わない
    • 最初のモジュールかつ最初の import として実行されないなら、実装は悪夢になり得る
  • この提案を見るたびに、プログラムが依然として正しく動作することをどう保証するのか気になる。既存のマルチスレッド Python コードのかなり多くは、安全でない書き方をされている
    特に、複数の企業のコードベースやオープンソースプロジェクトで繰り返し見てきた データ競合 が問題である。こうしたプログラムは、GIL が一度に1つのスレッドにしか実行を許さないことに暗黙に依存しているため、壊れずに済んでいるだけである
    GIL がなくなれば、こうしたプログラムは壊れるだろう。Python は動的型付け言語なので、既存の Python プログラムからこの種の問題を見つけ出せる静的解析器が存在するかは非常に疑わしい
    より起こりそうなのは、実行時に非決定的に現れる巧妙なバグである。いっそクラッシュしてくれればよいのだが、この種のバグは誤った動作をする結果につながる可能性が高い
    もしかすると、この GIL なしの提案は大半のプログラムで使うためのものではないのかもしれない。プログラマが GIL がないことを理解し、それに合わせて書けるごく一部の状況のための、極めて特化したツールなのかもしれない

    • データ競合のあるマルチスレッドプログラムなら、すでに問題がある。GIL がデータ競合を不可能にするわけではない
      GIL は、一度に1つのスレッドだけが Python バイトコードを実行できるという意味にすぎない。GIL のあるインタプリタでも、バイトコードの間でスレッドを切り替えることができ、多くの Python 演算は複数のバイトコードを必要とする。多くの人が「アトミック」だと思っている組み込み型の組み込みメソッドも含まれる
      だから Python は、現在 GIL があるにもかかわらず、ロック、ミューテックス、セマフォのようなものを提供している
    • ちょっと面白い事実として、GIL はすべての 競合状態バグ を決して防いでくれるわけではない
      GIL をめぐって競合するスレッドは、すでに悪いタイミングで互いに GIL を奪い合い、混乱を生み出すことがある
    • 要点は、ライブラリに nogil モード 対応の有無を宣言させること、つまりオプトインだと理解した
      すべての依存関係が許可した場合にだけプログラムが GIL なしで実行されるなら、そうしたバグを直す時間は十分にあるはずだ
    • no-GIL Python は、少なくとも3〜4回のリリースサイクルの後に来るように思う。3.11 が出てから1年が経ったが、プロダクションの Python コードはまだ 3.8 あたりが多いのではないかと思う
      そうすると、大規模にこの問題を扱うことになるのは、おそらく2030年が近づく頃だろう。今使っているランタイムを最新リリースへすぐに上げるプロダクションもあまり見かけない
      厳しく聞こえたいわけではないが、Steering Council は 2 から 3 への移行のようなものをもう一度は望んでいないと言っているので、人々が気軽にアップデートすることはないだろう。今オンラインにある内容の大半は、コピー&ペーストするには危険かもしれない
    • GIL はインタプリタだけを保護する。できることは問題の発生頻度を下げる程度にすぎない
      実際の Python コードにはスレッド関連のバグが非常に多い
  • OCaml も似たような進化を遂げたのではなかったか。2つのプロジェクトの間に比較できる点があるのか気になる

    • そうではないと思う。OCaml 5 は、グローバルロックを取り除いて既存コードを壊すのではなく、共有ロックを持つ1つ以上のスレッドを管理する domain という新しいプリミティブ機能を導入した
      そのため既存のスレッド 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、その他の高速化を得たい

    • Mojo がもっと完成した世界ならよかったのだが、今はその水準にはまったく近くない
    • 同意する。むしろ Rust、Nim、.NET で書き直す
    • -3 まで下がったダウンボートが、HN のダウンボートは論理ではなく 縄張り争い とプライドに関するものだということをよく示している
      Python コードを nogil Python に移したくないって? ならダウンボート、という感じだ
  • 名前を付けるなら python4python3-gilfoilpython3-gilfree などがあり得る

  • 現在、GIL なしの Python に注力している流れはかなり奇妙に感じる。Faster CPython チームは、リリースごとに CPython の性能を 50% 高めるという野心的な目標を掲げていた。
    3.11 では実際に改善があったが、50% にはほど遠く、私たちのテストのかなりの数では 3.12 は同程度か、むしろ遅かった。本物のマルチスレッドは素晴らしいだろうが、まずは シングルスレッド性能 が改善されることをずっと強く望んでいる。
    もちろん、私たちのニーズがすべての人を代表しているわけではないかもしれないことは認めるし、Python を素晴らしい言語にするために費やされたすべての作業には感謝している。それでも、自分が何を見落としているのか気になる。

    • Python は、複数コアの利用について早急な答えを持つ必要があると思う。AMD はつい先ほど 96 コア CPU を発売した。
      現状、複数コアの利用は multiprocessing を通じて行われるが、制約が多い。複数インタプリタがコルーチンのようなものと一緒に来る可能性があるのは理解しているが、それでも本物のマルチスレッドという選択肢のほうが好ましい。
    • この 2 つはまったく別の目標だ。理論上、マルチスレッド Python は特定のプログラムを高速化できるが、そのやり方が重要になる。
      nogil Python では、たとえば Python オブジェクトとしてアクセス可能な共有状態を持ちながら、複数のスレッドが C コードを呼び出せる。これは 機械学習 ではかなり重要で、実際に今回の PEP の現在の形は PyTorch チームから出てきたものだ。
      シングルスレッド性能も重要だが、重要な箇所については、すでに numba、Cython、そして Mojo のようなかなり良い回避策があった。
      順序も重要だ。nogil が導入されると、faster CPython の作業のかなりの部分が完全に捨てられる可能性があるため、チーム間で調整する必要があった。
      理想的な世界なら、nogil モードとシングルスレッド性能改善の両方がある形だ。Guido も高度な JIT を検討中だと示唆していた。
    • 「Python」で計算コストが大きい部分は、numpy や tensorflow のようなライブラリで処理される。
      Python は、低レベルの抽象化をより高水準の言語から扱うことを非常に楽にしてくれる。だから長年の Python 開発者として、GIL についてはそれほど大きなストレスを感じてこなかった。
    • PEP 703 で引用されている人たちのニーズと、あなたのニーズが違うという点を見落としているように思う: <https://peps.python.org/pep-0703/#motivation>
    • 私の理解では、この 2 つは CPython 内の別々のプロジェクトで、必ずしも同じ人たちが作業しているわけでもない。
      どちらか一方だけを選ばなければならないなら、ほとんどのユースケースには単により速いシングルスレッドコードのほうが合っている、という点には同意する。ただし、両方を持ってはいけない理由もない。
  • hindsight では明らかだが、Python 側が 2 から 3 への移行がどれほど長く苦痛を伴うものになるかを知っていたなら、インタプリタ内部もはるかに大きく手を入れていたのではないかと思う。
    12 年がかりの移行を経たにもかかわらず、シングルスレッド性能 はいまだにひどく、本物のマルチスレッドに到達するには、まだ何度か苦痛を伴う移行が残っている。
    オープンソース開発には寛容であるべきだとは分かっているが、どの時点から「非常に管理のまずい言語」と呼んでもよいのか疑問に思う。

    • 管理がひどいわけではない。Python には問題が多いが、それらはすべて Python の成功 から生じた問題だ。
      Python の最悪の部分は、Python があまりにも人気でエコシステムがあまりにも大きいために変更が難しい部分だ。だから、あらゆる種類の変更が後方互換性のためにより難しくなる。
    • その通りだ。これほど長い時間が経ったのに、multiprocessing はいまだにひどい。
      Python をあまりにもすぐ擁護しがちな傾向がある。偏りなく客観的に見ることが重要だ。
    • どこかの時点で、Python と構文は同じだが、最初からより良い性能とスレッド対応のために設計された言語を作るべきなのではないかと思う。
      性能と Python 構文を求めるプロジェクトはそちらへ行けばよい。現在の Python は複数の目標の間でもがき、どれもきちんと達成できていないように見える。
    • 2 から 3 への移行が長く悪いものになることは予測可能で、当時も人々はそう言っていた。
      Perl 5/6 が例として挙げられていた。誰も移行しないことが明らかになった後でさえ、それをより簡単にしようとする試みまでにさらに約 5 年かかった。