LL言語がマルチプロセッサ環境のメリットを捨ててまでグローバルインタプリタロックを採用している理由について調べてみた。
その為にはプロセスとスレッドについての前提知識がけっこう必要だったので、ついでにざっくり調べてみた。
LL言語がマルチプロセッサ環境のメリットを捨ててまでGILを実装している理由
結論を先に書くと、LL言語がマルチプロセッサ環境のメリットを捨ててまでGILを実装している一番の理由は、「スレッドセーフではないCで書かれたモジュールをたくさん使っているから」ということになるっぽい。
この結論に至るまでの色々な前提知識についても書いておく。
プロセスとスレッド
プロセスとスレッドの特徴をざっくり対比させて書くとこんな感じになる。
- プロセスは実行されたプログラムの実体
- プロセスは固有のメモリ空間を持つ
- プロセスはコンテキストスイッチのコストがスレッドに比べると大きい
- スレッドはプロセスをさらに細かくした実行単位
- スレッドは共有のメモリ空間を持つ
- スレッドはコンテキストスイッチのコストがプロセスに比べると小さい
ユーザースレッドとカーネルスレッド
ユーザースレッド
ユーザ空間で実装されたスレッド機構をユーザースレッドと呼ぶ。
1つのプロセス内の複数のスレッドは常に1つだけが動作していることになるため、
ユーザースレッドはあくまでもプログラミングの手法としてのみ意味を持ち、性能向上に寄与するものではない。
また、ユーザースレッドのうち、特にVM上で動くものをグリーンスレッドと呼ぶ。
カーネルスレッド
カーネル空間で実装されたスレッド機構をカーネルスレッドと呼ぶ。
マルチプロセッサシステムであれば同じプロセス内の複数のスレッドを並行して実行することができる。
プロセス管理から見ればプロセスとほとんど変わりないため、オーバヘッド(コンテキストスイッチなど)もプロセス並みとなる。
ライトウェイトプロセス
ライトウェイトプロセス(LWP)とは、スレッドを複数並行して実行するためのカーネル内の機構のことであり、
マルチプロセッシングにおいて、ひとつのプロセス内のスレッドを複数個同時に実行する仕組み。
カーネルスレッドとLWPを総称してネイティブスレッドと呼ぶこともある。
グリーンスレッドとネイティブスレッド
グリーンスレッド
グリーンスレッドとは、OSではなくVMによってスケジュールされるスレッドのこと。
グリーンスレッドはネイティブのOSの機能に依存せずにマルチスレッド環境をエミュレートする。
ネイティブスレッド
ネイティブスレッドとは、OSによってスケジュールされるスレッドのこと。
ネイティブスレッドはネイティブOSの機能を使って処理を複数のプロセッサに割り当てることができる。
スレッドセーフ
スレッドセーフとは、マルチスレッドプログラミングにおける概念のこと。
あるコードがスレッドセーフであるという場合、そのコードを複数のスレッドが同時並行的に実行しても問題が発生しないことを意味する。
一例として、1つのグローバル変数と2つのスレッドがあり、1つのグローバル変数をそれぞれのスレッドが意図しないタイミングで書き換え、 もう一方のスレッドにとって不都合が起きるようなコードは、スレッドセーフではない。
Ruby, Python, Javaのスレッド実装
Ruby(>1.9)
ネイティブスレッドを用いて実装されているが、 現在の実装では Ruby VMはGiant VM Lock(GVL)を有しており、 同時に実行されるネイティブスレッドは常に1つとなる。
Python
Rubyとほとんど同じ。Global Interpreter Lock(GIL)が実装されおり、同時に実行されるネイティブスレッドは常に1つとなる。
一例として、Pythonで100個スレッドを作ると、101個のスレッドが作成される。
そのうち1つはインタプリタのメインスレッドとなっている。
(pythonのスレッドの扱い より)
Java(>1.4)
ネイティブスレッドを用いて実装されている。
グローバルインタプリタロック
ようやく、本題のグローバルインタプリタロックについて。
グローバルインタプリタロック(GIL)とは、LL言語のインタプリタのスレッドによって保持されるスレッドセーフでないコードを、他のスレッドと共有してしまうことを防ぐための排他ロックのこと。
インタプリタのひとつのプロセスごとに必ずひとつのGILが存在する。
GILのデメリット
同時に実行できるスレッドが1つに制限されてしまうため、マルチプロセッサの恩恵を受けることができない。
GILのメリット
通例スレッドセーフではないC言語のライブラリとの結合が容易であり、 シングルスレッドのプログラムの速度向上(すべてのデータ構造に対して別々にロックを獲得・開放する必要がなくなる)がある。
というわけで、LL言語がマルチプロセッサ環境のメリットを捨ててまでGILを実装している一番の理由は、「スレッドセーフではないCで書かれたモジュールをたくさん使っているから」ということになるっぽい。
参考リンク
軽量スレッドブームだと思うので、そこらへんの情報をまとめてみる