Slide 1

Slide 1 text

Pythonスレッドとは結局何なのか? CPython実装から見るNoGIL時代の変化 PyCon JP 2025.9.26 (DAY1) 12:00 - 12:30 Recustomer株式会社 CTO Shugo Manabe @curekoshimizu

Slide 2

Slide 2 text

Profile : 眞鍋 秀悟 ( X: @curekoshimizu ) 略歴 ● 京都大学 / 大学院 ○ 入試一位合格 ○ 数学系 (高速な計算方法を専門) [今回のお話と少し関係が深い] ● Fixstars ○ Executive Engineer ● Mujin ○ Architect ● Preferred Networks ○ Engineering Mananger ● Hacobu ○ 研究開発部部長・CTO室室長 ● want.jp ○ VPoP ● [Now] Recustomer ○ CTO かなり長い間 Pythonを 業務で使ってきた 2 昨年は 「四則演算のCPythonでの内部実装」 という話を PyCon JP 2024 で発表させていただきました。

Slide 3

Slide 3 text

Thread 3

Slide 4

Slide 4 text

人々を魅了し 高速化する技術 4

Slide 5

Slide 5 text

そんなThreadに 衝撃ニュースが 5

Slide 6

Slide 6 text

Python3.13の衝撃 6

Slide 7

Slide 7 text

GIL無効化できます! (ただし実験的機能) 7

Slide 8

Slide 8 text

Python3.14では さらに 8

Slide 9

Slide 9 text

実験モードから昇格して GIL無効化 公式サポート化 (ただし規定ビルドは従来どおり) 9

Slide 10

Slide 10 text

つまりは NoGIL (GIL無効化) というキーワードを Python界隈を騒がせました 10

Slide 11

Slide 11 text

GILとは何なのか? NoGILは何がすごいのか? この高速化の歴史的転換点を Threadとともに説明します! 11

Slide 12

Slide 12 text

まずは基本的なおさらいから (正確性には多少目をつぶった説明をします) 12 (注.) 時間の兼ね合いで、不正確なことを述べている点も多々あります。 例えば、 ● 組み込みOS ● 1プロセスで複数CPUを使っている場合の話 ● ハイパースレッディング ● アダマールの法則 ● 並列処理と平行処理の違い などいろいろ考慮すべきこともあるのだが、 大雑把には正しいと言えるので、このまま続けて議論を続ける。

Slide 13

Slide 13 text

逐次処理 - 並列ではなく、処理が終わったら次の処理をする 13 処理A 処理B 処理C 処理D 時間

Slide 14

Slide 14 text

並列処理 14 時間 高速化のために ものごとを並列で終わらせたい 処理A 処理B 処理C 処理D

Slide 15

Slide 15 text

プロセス処理 は 逐次処理 ではない 15 時間 OSは プロセスA (メール送信) と プロセスB (Pythonコード実行) の実行は同時にできるので 逐次処理ではないはず プロセスA プロセスB プロセスC プロセスD

Slide 16

Slide 16 text

プロセス と 並列処理 16 時間 メールを送りながらも Pythonのコード実行は同時に できているはずなので この動きが正しそう プロセスA プロセスB プロセスC プロセスD

Slide 17

Slide 17 text

プロセスには「計算」という CPUを利用する では、1CPUしかなかったら? 17

Slide 18

Slide 18 text

並列処理 - Q. CPU (計算資源) が 1個 のときはどうなる? 18 時間 メールを送りながらも Pythonのコード実行は同時に できているはずなので こうなっていそう プロセスA プロセスB プロセスC プロセスD

Slide 19

Slide 19 text

OSが 処理を切り替えながら (ディスパッチしながら) 並列処理をしているように みせている 19

Slide 20

Slide 20 text

並列処理 - Q. CPU (計算資源) が 1個 のときはどうなる? A. OS が切り替えながら 疑似並列により動作する 20 プロセスA プロセスB プロセスC プロセスD 時間 1個のCPUリソースを それぞれのプロセスで 切り替えながら使う

Slide 21

Slide 21 text

並列処理 - 2コアCPU における プロセス の動作の動きイ メージ 21 プロセスA プロセスB プロセスC プロセスD 時間 2コアあるので2プロセス同時に動ける

Slide 22

Slide 22 text

並列処理 - 複数コアにおけるプロセスの動作イメージ 22 プロセスA プロセスB プロセスC プロセスD 時間 それぞれのプロセスがそれぞれの CPUコアが割り当てられて 並列に計算できる!

Slide 23

Slide 23 text

プロセスと並列についてざっくりまとめ ● プロセスは OS によって管理される ● OS によっていい感じに、切り替えられながら並列処理されているように動作でき る ● CPU コアという計算資源があれば、実際並列度が高まる 23 (注.) 時間の兼ね合いで、不正確なことを述べている点も多々あります。 例えば、 ● 組み込みOS ● 1プロセスで複数CPUを使っている場合の話 ● ハイパースレッディング ● アダマールの法則 ● 並列処理と平行処理の違い などいろいろ考慮すべきこともあるのだが、 大雑把には正しいと言えるので、このまま続けて議論を続ける。

Slide 24

Slide 24 text

プロセスだけの時代からスレッドの時代へ ● プロセスは独立したメモリ空間をもっている (安全性が高い ) ○ そのため、プロセス同士が干渉して破壊するようなこともない ● プロセス生成コストが高く遅い ● プロセス間通信 (IPC) のオーバーヘッドが大きい 24 並列処理をするには 「安全だが重たい」

Slide 25

Slide 25 text

プロセスだけの時代からスレッドの時代へ ● プロセスは独立したメモリ空間をもっている (安全性が高い ) ○ そのため、プロセス同士が干渉して破壊するようなこともない ● プロセス生成コストが高く遅い ● プロセス間通信 (IPC) のオーバーヘッドが大きい 25 並列処理をするには 「安全だが重たい」 1980年頃: GUIの登場等で たくさんの並列処理・プロセス間通信処理が求められる時代に

Slide 26

Slide 26 text

● スレッドはメモリを共有 (安全ではない ) ● スレッド間の変数の共有等が簡単 ● 生成のオーバーヘッドが低い プロセスだけの時代からスレッドの時代へ ● プロセスは独立したメモリ空間をもっている (安全性が高い ) ○ そのため、プロセス同士が干渉して破壊するようなこともない ● プロセス生成コストが高く遅い ● プロセス間通信 (IPC) のオーバーヘッドが大きい 26 並列処理をするには 「安全だが重たい」 軽量でデータの共有し やすさが求められた。 それがスレッド 1980年頃: GUIの登場等で たくさんの並列処理・プロセス間通信処理が求められる時代に

Slide 27

Slide 27 text

プロセスだけの時代からスレッドの時代の流れ 27 プロセス だけの 時代

Slide 28

Slide 28 text

プロセスだけの時代からスレッドの時代の流れ 28 プロセス だけの 時代 スレッドを 各種ベンダーが 独自に ● SunOS : lwp (lightweight processes) ● DEC:DECthreads ● カーネギーメロン: Mach kernel など

Slide 29

Slide 29 text

プロセスだけの時代からスレッドの時代の流れ 29 プロセス だけの 時代 スレッドを 各種ベンダーが 独自に POSIX Threads (pthreads) の 規格化 ● SunOS : lwp (lightweight processes) ● DEC:DECthreads ● カーネギーメロン: Mach kernel など pthreads 重要ワード

Slide 30

Slide 30 text

プロセスだけの時代からスレッドの時代の流れ 30 プロセス だけの 時代 スレッドを 各種ベンダーが 独自に POSIX Threads (pthreads) の 規格化 ● SunOS : lwp (lightweight processes) ● DEC:DECthreads ● カーネギーメロン: Mach kernel など Linux, MacOS, FreeBSD などに pthreads は Cライブラリとして 標準搭載 Windows以外 標準搭載と いっても 過言ではない

Slide 31

Slide 31 text

CPython の threading.Thread の実装は? 31 ● POSIX系 : pthreads を利用 (だいたいこれが使われると思うとよい ) ● Windows環境: Win32スレッドAPI (NT threads) を利用 ● WASM環境:環境依存 (今回の発表では省略 ) thread.c より抜粋

Slide 32

Slide 32 text

CPython の threading.Thread の実装は? 32 from threading import Thread thread = Thread(target=worker) thread.start() このシンプルなコードで 一体何が起こっているのか CPython本体の 実装を見ていきましょう

Slide 33

Slide 33 text

CPython の threading.Thread の実装は? 33 from threading import Thread thread = Thread(target=worker) thread.start() このシンプルなコードで 一体何が起こっているのか CPython本体の 実装を見ていきましょう

Slide 34

Slide 34 text

CPython の threading.Thread は pthreads のラッパー 34 ● Thread.start() Lib/threading.py Python層:高レベルAPI threadingライブラリ層

Slide 35

Slide 35 text

CPython の threading.Thread は pthreads のラッパー 35 ● Thread.start() Lib/threading.py Python層:高レベルAPI threadingライブラリ層 ● thread_Python_start_joinable_thread() Modules/ _threadmodule.c C拡張層:低レベルAPI _threadライブラリ層 (import _thread可) call

Slide 36

Slide 36 text

CPython の threading.Thread は pthreads のラッパー 36 ● Thread.start() Lib/threading.py Python層:高レベルAPI threadingライブラリ層 ● thread_Python_start_joinable_thread() Modules/ _threadmodule.c C拡張層:低レベルAPI _threadライブラリ層 (import _thread可) call import _thread help(_thread.start_new_thread) 実行すべきではないが 確かに Pythonからも 使える _thread モジュール

Slide 37

Slide 37 text

CPython の threading.Thread は pthreads のラッパー 37 ● Thread.start() Lib/threading.py Python層:高レベルAPI threadingライブラリ層 ● thread_Python_start_joinable_thread() Modules/ _threadmodule.c C拡張層:低レベルAPI _threadライブラリ層 (import _thread可) ● Python_start_joinable_thread() ○ pthread_create() : POSIX Threads 呼び出し Modules/thread_pthread.h POSIX層 結局のところ pthreads に移譲している処理 call call

Slide 38

Slide 38 text

CPython の threading.Thread と pthreads 38 ● pthreads の中身概要 ○ Mac:Mach kernel ラッパー ■ Macのkernelはハイブリッドカーネルで一部は Mach kernel ○ Linux 系:NPTLライブラリ (Native POSIX Thread Library) ■ スレッドをプロセスのようにカーネル空間に生成できるので、スケジューリングなどはプロ セスのような管理体型 歴史の伏線回収

Slide 39

Slide 39 text

CPython の threading.Thread と pthreads 39 ● pthreads の中身概要 ○ Mac:Mach kernel ラッパー ■ Macのkernelはハイブリッドカーネルで一部は Mach kernel ○ Linux 系:NPTLライブラリ (Native POSIX Thread Library) ■ スレッドをプロセスのようにカーネル空間に生成できるので、 スケジューリングなどはプロ セスのような管理体型

Slide 40

Slide 40 text

CPython の threading.Thread と pthreads 40 ● pthreads の中身概要 ○ Mac:Mach kernel ラッパー ■ Macのkernelはハイブリッドカーネルで一部は Mach kernel ○ Linux 系:NPTLライブラリ (Native POSIX Thread Library) ■ スレッドをプロセスのようにカーネル空間に生成できるので、 スケジューリングなどはプロ セスのような管理体型 つまり、 「プロセス」のように OS がいい感じに ディスパッチしながら並列処理してくれる

Slide 41

Slide 41 text

CPython の threading.Thread と pthreads 41 ● pthreads の中身概要 ○ Mac:Mach kernel ラッパー ■ Macのkernelはハイブリッドカーネルで一部は Mach kernel ○ Linux 系:NPTLライブラリ (Native POSIX Thread Library) ■ スレッドをプロセスのようにカーネル空間に生成できるので、 スケジューリングなどはプロ セスのような管理体型 OSスレッドA OSスレッドB OSスレッドC OSスレッドD 時間 それぞれスレッドが それぞれのCPUコアに 割り当てられれば 並列に計算できる!

Slide 42

Slide 42 text

CPython の threading.Thread と pthreads 42 そう、実は、 GIL さえなければ 正しい動き

Slide 43

Slide 43 text

GIL なしの世界では概ね正しい 43 GILがない世界では概ね正しく、 CPUコアがたくさんあれば、スレッドは並列動作する ので高速 OSスレッドA OSスレッドB OSスレッドC OSスレッドD 時間 それぞれスレッドが それぞれのCPUコアに 割り当てられれば 並列に計算できる! この部分の説明の 正しさについて GILがこれを妨げているという話

Slide 44

Slide 44 text

ずっと説明を 保留してきた GIL の話に 入っていきます 44

Slide 45

Slide 45 text

GIL = Global Interpreter Lock 45

Slide 46

Slide 46 text

GIL が 並列性能を落としてでも 導入された目的とは? 46

Slide 47

Slide 47 text

GIL (Global Interpreter Lock) が防ぎたいこと 47 ● GIL という仕組みが必要だったのは? ○ GC 処理といったメモリー管理機構をスレッドセーフにするため

Slide 48

Slide 48 text

GIL (Global Interpreter Lock) が防ぎたいこと 48 ● GIL という仕組みが必要だったのは? ○ GC 処理といったメモリー管理機構をスレッドセーフにするため 時間 スレッド A スレッド B どちらからも 触れる 変数x 使用 開始

Slide 49

Slide 49 text

GIL (Global Interpreter Lock) が防ぎたいこと 49 ● GIL という仕組みが必要だったのは? ○ GC 処理といったメモリー管理機構をスレッドセーフにするため 時間 スレッド A スレッド B どちらからも 触れる 変数x 使用 開始 使用 完了 使用 開始 同タイミングで発生

Slide 50

Slide 50 text

GIL (Global Interpreter Lock) が防ぎたいこと 50 ● GIL という仕組みが必要だったのは? ○ GC 処理といったメモリー管理機構をスレッドセーフにするため 時間 スレッド B どちらからも 触れる 変数x 不要なので 削除した 何故か使えな くなっている 使用 開始 同タイミングで発生 参照カウントが スレッドセーフではなく、 メモリー管理機構が壊れる という話 使用 完了 使用 開始 スレッド A

Slide 51

Slide 51 text

大雑把な GIL の挙動 - CPUコアがいくらあろうとも GILによって各スレッドは決して同時には動作「させない」 51 スレッド1 スレッド2 スレッド3 スレッド4 時間

Slide 52

Slide 52 text

もうちょっと詳しい GIL の挙動 (thread_run関数) 52 52 OSスレッド生成 GIL確保 目的の関数実行 GIL解放

Slide 53

Slide 53 text

もうちょっと詳しい GIL の挙動 (thread_run関数) 53 53 OSスレッド生成 GIL確保 目的の関数実行 GIL解放 Python バイトコード Python バイトコード Python バイトコード Python バイトコード

Slide 54

Slide 54 text

もうちょっと詳しい GIL の挙動 (thread_run関数) 54 54 OSスレッド生成 GIL確保 目的の関数実行 GIL解放 Python バイトコード Python バイトコード Python バイトコード Python バイトコード 各バイトコードの処理実行の中で 「GILの確保 or 解放」判定処理がある 別のスレッドが確保要求を出している → 一旦自分は GIL解放して、 sleep、その 後別のスレッドに対して、自身の GIL取 得要求。 sleep時間は sys.getswitchinterval() で 約5ms GIL取得しないと Pythonのバイトコード は実行できない eval_breaker

Slide 55

Slide 55 text

つまり、 Pythonのバイトコードの 実行部分を スレッド並列にしなければ 安全だ というのがGILの思想 55

Slide 56

Slide 56 text

もっと詳しく言うと、 1CPUのときと同様な動作にすれば 参照カウントを同時に アクセスしないから メモリー管理は壊れないよね? 疑似並列でスレッドを 実行しよう! というのがGILの思想 56

Slide 57

Slide 57 text

同時に「使用・未使用」が発生し ていたから問題が起こっていた これがなくなる! 57

Slide 58

Slide 58 text

このような ロジックになった GILが生まれた 時代背景とは? 58

Slide 59

Slide 59 text

Python 1.14 ~ 1.15 (およそ 1996~1998頃) Python 1.14 にて 「--with-thread」オプションが追加され スレッド を考慮できるよ うに Python1.15 のリリースで GIL (global interpreter lock) は登場している 59

Slide 60

Slide 60 text

青グループ Intel CPU の 歴史 と 時代背景 60 周波数を上げていけば性能は 上がるぞ!時代 (1CPU = 1コア時代) 486 Pentium (P5・P6) Pentium (NetBurst)

Slide 61

Slide 61 text

青グループ Intel CPU の 歴史 と 時代背景 61 周波数を上げていけば性能は 上がるぞ!時代 (1CPU = 1コア時代) Python GIL誕生 (1996~1998頃) 486 Pentium (P5・P6) Pentium (NetBurst)

Slide 62

Slide 62 text

青グループ Intel CPU の 歴史 と 時代背景 62 周波数を上げていけば性能は 上がるぞ!時代 (1CPU = 1コア時代) Python GIL誕生 (1996~1998頃) 486 Pentium (P5・P6) Pentium (NetBurst) 周波数だけで 性能があがらな くなる Intel Pentium4 が 4GHzを諦めた 象徴的ニュース

Slide 63

Slide 63 text

青グループ Intel CPU の 歴史 と 時代背景 63 周波数を上げていけば性能は 上がるぞ!時代 (1CPU = 1コア時代) Python GIL誕生 (1996~1998頃) 486 Pentium (P5・P6) Pentium (NetBurst) 周波数だけで 性能があがらな くなる Core シリーズ Core i シリーズ 2コア・4コア 時代 (Core2Duo) 複数コア時代 (Core i5・i7) CPUが複数の 物理コア時代 (2006年頃)

Slide 64

Slide 64 text

つまり Python は マルチコア時代 じゃないときに この問題の対応に 迫られていた (Rubyも同様) 64

Slide 65

Slide 65 text

今の時代、CPUがたくさんあるものの GILというものがあるので CPU1個しか使われていない 65 複数のCPUコアの恩恵を得るために NoGILを利用したいという話

Slide 66

Slide 66 text

最初のスライドを 思い出しましょう 66

Slide 67

Slide 67 text

Python3.13と3.14 の衝撃 67

Slide 68

Slide 68 text

GIL無効化できます! 68

Slide 69

Slide 69 text

そう これは高速化界隈に とって 衝撃なのです 69

Slide 70

Slide 70 text

GIL無効化の実現には どんな努力が あったのでしょうか? 70

Slide 71

Slide 71 text

残念ながら 時間の都合もあり 細かくお話することは できません 71

Slide 72

Slide 72 text

ここからは 雰囲気で理解し CPythonに感謝する気持ち を持って返りましょう 72

Slide 73

Slide 73 text

GILありだと「参照カウント」という一つのフィールドだったものが ● local用の参照カウント ● shared用の参照カウント に分割されている 参照カウンタの実装の変更 73

Slide 74

Slide 74 text

objectを生成したスレッドが所有スレッド ● local用の参照カウント : 所有スレッドが更新する用 ○ 絶対に自分しか更新しないので、気にせず更新できる ● shared用の参照カウント : その他のスレッドが更新する用 ○ 複数のスレッドから更新されても大丈夫な AtomicなRead-Modify-Write命令が必要な ため、処理コストがかかる どちらかが 0になったりすると、統合要求などを経て、 objectを削除することになる 参照カウンタの実装の変更 74 sys.getrefcount(x) というリファレンスカウントを返す関数は、 local + shared の値を返すなど、 GILとNoGILで結果が異なる点も面白い

Slide 75

Slide 75 text

● list 型の append() 等の結果、サイズが増減する処理を考える コレクション型のサイズ変更等のイベント 75 x y z - - - - - - [x, y, z] を表す メモリー領域 別のところで 利用されている メモリー領域 誰も使っていない 空間 w を追加したいのだが、 連続領域に 格納できない

Slide 76

Slide 76 text

● list 型の append 等のサイズが増減する処理を考える コレクション型のサイズ変更等のイベント 76 x y z - - [x, y, z] を表す メモリー領域 x y z w 別のところで 利用されている メモリー領域 [x, y, z, w] を 連続メモリー領域を作成して x, y, z はコピーされる この領域を破棄したいのだが 別のスレッドが このobjectを使っている かもしれず、 安易に破棄できない

Slide 77

Slide 77 text

list型のオブジェクトは多数ありそれらをすべて Lockで同期で取るのは大変 QSBR(Quiescent State-Based Reclamation)という技法が利用されている。 ● 各スレッドが、クリティカルセクションを抜けて、古いデータ構造を参照していない ことを、定期的に報告させる ○ eval_breaker がこのポイントになっている ● これにより、古いデータは誰からも参照されないことを保証し、遅延削除  QSBR (Quiescent State-Based Reclamation) 77

Slide 78

Slide 78 text

● GILなしモードには、複数のスレッドからオブジェクトの更新が行われる可能性が あり、それを Lockするために ob_mutex というロック機構が Object に必ず追加 されている ● Object という単位で Lockを持っているため、 1つ1つのLockを高速に行う必要が あったため NoGILで作られた「 PyMutex」型 Objectに必ず存在するPyMutex 78

Slide 79

Slide 79 text

● 従来の Lock型は 100Byte 程の大きさであり、オブジェクト一つ一つに持たせる には大きかった ● PyMutex は 1Byte というとても小さなデータでロックを表す PyMutex : 軽量なロック機構 79 旧来のLock機構 (Python/thread_pthread.h) 新しい PyMutex (Python/thread_pthread.h)

Slide 80

Slide 80 text

Lockの待機方法のイメージ 80 Object A Object B Object C Thread X Thread Y Thread W Thread Z それぞれ Lock をとりたい 「Object」 という 「お店」の前に 「Thread」なる「人」が行列を つくる方式ではなく 待機列 (人) Object (お店)

Slide 81

Slide 81 text

Lockの待機方法のイメージ 81 Object A Object B Object C Thread X Thread Y Thread W Thread Z 駐車場 Object (お店) 駐車場に 待機あり 駐車場に 待機あり 駐車場に 待機なし 席が空いたら 駐車場に待っているスレッドに 呼び出しが行われるような イメージ

Slide 82

Slide 82 text

Lockの待機方法のイメージ (Parking Lot) 82 Object A Object B Object C Thread X Thread Y Thread W Thread Z 駐車場 Object (お店) 駐車場に 待機あり 駐車場に 待機あり 駐車場に 待機なし 待機列がないものについては 何回か (40回)、Lockをとるのを試みる。 Thread V

Slide 83

Slide 83 text

Lockの待機方法のイメージ (Parking Lot) 83 Object A Object B Object C Thread X Thread Y Thread W Thread Z 駐車場 Object (お店) 駐車場に 待機あり 駐車場に 待機あり 駐車場に 待機あり それでも取れなかったら 邪魔になるので駐車場にいく Thread V

Slide 84

Slide 84 text

Parking Lot アルゴリズム APIと名称は WebKit の WTF::ParkingLot (Web Template Framework)と Linux の futex API に参考にされてつくられたもの 汎用Lockに比べて高速になっており、 Lockを表すサイズも小さい 84 Include/internal/pycore_parking_lot.h

Slide 85

Slide 85 text

本当は もっともっと語りたい CPython GIL無効化の歴史!!! 85

Slide 86

Slide 86 text

時間がないので ここまで出てきた キーワードだけ 覚えておきましょう 86

Slide 87

Slide 87 text

● 参照カウントの領域分割 ● QSBR ● Objectに1つ1つにLock情報 ● 軽量Lockの実装 ● ParkingLotアルゴリズム などの力で NoGIL 実現に至りました 87

Slide 88

Slide 88 text

細かい話から戻りましょう 88

Slide 89

Slide 89 text

そもそも我々は 89

Slide 90

Slide 90 text

GIL無効化を 手に入れるまでの この長い歴史の中 90

Slide 91

Slide 91 text

CPythonで 複数CPUの恩恵は うけてこなかったのか? 91

Slide 92

Slide 92 text

これまでの高速化技術 1. プロセス生成  92

Slide 93

Slide 93 text

NoGIL以前からある高速化技法 1. プロセス生成 93 ● multiprocessing.Process・concurrent.futures.ProcessPoolExecutor を使って プロセスを作る CPython 実行プロセス Process A Process B プロセス番号が違うので GILが及ばない! プロセスとCPUのアサインについては 述べた通り効果的。 しかしながら、 プロセスはスレッドと違い、 データの共有が難しい

Slide 94

Slide 94 text

NoGIL以前からある高速化技法 1. プロセス生成 94 Uvicorn の例 ● FastAPI ● Django Ninja ● Starlette などで利用される Web サーバー $ uvicorn project.asgi:application --workers 4 GIL を防ぐために プロセスを増やして実行されている

Slide 95

Slide 95 text

これまでの高速化技術 2. マルチCPUに対応した ライブラリに任せる  95

Slide 96

Slide 96 text

NoGIL以前の高速化 2. マルチコアCPUに対応しているライブラリに任せる 96 ● numpy などのライブラリは 複数のCPUコアを用いた計算に対応済み ● 特に numpy に至っては、 BLAS・LAPACK といった 各 CPU に向けてとても チューニングされたライブラリ が呼び出されており、 人力でnumpyの演算より高 速に実行することは極めて難しい。 ● 劇的にチューニングされているライブラリには頼ったほうがいい

Slide 97

Slide 97 text

これまでの高速化技術 3. スレッドがI/Oネック を解消してくれていた  97

Slide 98

Slide 98 text

NoGIL以前の高速化 3. CPUではなく I/O ネックな処理をスレッドに任せる 98 ● あくまでも、 GIL によって並列化できないのは Python のバイトコード処理 ● 裏側の「ネットワーク転送待ち」のような I/O 待ちは GILの影響はなく 、裏側で待 たれるので、スレッドにする価値がある

Slide 99

Slide 99 text

最後のテーマ 99

Slide 100

Slide 100 text

同様の問題を抱える Ruby は GIL に どう対応するのか? 100

Slide 101

Slide 101 text

Ruby の GIL 対応は? 2024.09.26 に Ruby の作者である まつもとゆきひろ氏 は次のように語っている 
 
 Rubyでは 静観しよう と思っているのには訳があります。AI方面 でGILによる問題に直面しているPythonと比較して、Rubyでは 重大な問題が発生していないのです。背景の一つとして、 Rubyが利用されているのが、Webアプリケーションの開発 が 多いことがあります。 
 
 引用: https://active.nikkeibp.co.jp/atcl/act/19/00484/080100015/?P=6 
 101

Slide 102

Slide 102 text

つまりは Ruby の立場はこういうこと 102 ● Ruby = Web 開発向け ● Web 開発向け = I/O 処理ネックになりがち ● GILがあっても I/O 処理は スレッドで高速になる ● Webサーバーであればプロセスを独立させることもできるので CPUは活かせる ということなんだと思います

Slide 103

Slide 103 text

対して AIや科学技術計算、 ロボットなど Web系だけにとらわれない Pythonは? 103

Slide 104

Slide 104 text

これらの分野はI/Oというより、 本当にCPUがネックになる処理が 求められることも多い 104

Slide 105

Slide 105 text

色々な分野 幅広いPythonという特性が NoGIL を求めたのかも しれません 105

Slide 106

Slide 106 text

我々はNoGILの使われ方の 歴史の目撃者なのです 一緒にこのPython界隈の 今後を楽しみにしましょう 106

Slide 107

Slide 107 text

107 エンジニア9名しかいません