学んだこと実装したこと

モチベーション維持のための備忘録

PythonのGIL(Global Interpreter Lock)

Scikit-learnにあるSAG(Stochastic Averaged Gradient)のコードを読んでたら、"nogil"という見慣れない単語が出てきたので調べた。

  • nogilはGIL(Global Interpreter Lock)をリリースするためのもの
  • CythonではGILの取得とリリースをプログラマが管理する必要がある
  • Pythonインタプリタを必要としない部分ではGILをリリースしたほうが全体として速くなる(ようである)
調べ事に至った経緯

勾配法などの最適化アルゴリズムをCythonで高速化してみたくなった。
でもCythonでNumpyやScipyを効率よく使う方法を知らないなあと思って、Scikit-learnの最適化アルゴリズムを読んでみることにした。
読み進めていると、"nogil"という見慣れない単語があったから意味を調べてみた。

cdef double loss(self, double p, double y) nogil:

github.com

GIL(Global Interpreter Lock)

nogilというのは、GILをリリースするためのものらしい。GILって何???
外部の C のコードにインタフェースする — Cython 0.17.1 documentation


CPython(Python処理系のC言語による実装)には「一つのインタプリタは同時に二つ以上のバイトコードを実行しない」という原則があるといった話は聞いたことあって、その実現に使われる機構の話のようだった。
要は、あるスレッドがPythonバイトコードを実行するには、インタプリタをロックして他のスレッドが使わないことを保証しないといけない。
Glossary — Python 3.8.0a0 documentation


でもこの実装だと、マルチスレッドにしてもPythonのコードを同時に処理できるわけじゃないから、高速化できない場合もある。
そんなときにはマルチプロセスにしてしまって(インタプリタを複数用意して)高速化するという手段もあるとのこと。
まあ別プロセスになるとデータの共有や受け渡しが面倒になりそうなんだけど、その方法までは調べていない。
blog.karky7.com


CythonとGIL

CythonはコンパイルするとCのソースコードができるけども、Pythonバイトコードのラッパーが内部にある可能性がある、はず。
つまり、Pythonの処理系を呼び出す可能性があるので、Cythonで書かれた部分はデフォルトでGILを取得する。
しかも、Cython内ではGILを自分で管理(必要に応じてリリース・再取得)する必要があるらしい。
それをしないと何がことが起こるかは下記リンクを参考にした(nogilを使ってないけど)。
blog.bonprosoft.com


確かに、Pythonの処理系を使わないとわかりきっている場所ならば、GILをリリースして他のスレッドに使ってもらった方が高速になるはず。
Numpyとnogilを使った実験が下記リンクにあるけど、コードをちゃんと読んでないからなんとも言えない。
結論にはCython+マルチスレッド+nogilが経験的によくてMKLとの比較も必要ってあるけど、普通のNumpyには並列化の恩恵がないのだろうか?
Anacondaとかに入ってるMKLを使ったNumpyだと密行列の行列積とかは並列化されてとても速くなるから、自分でマルチスレッドにしてしまうと逆に遅くなってしまうような気もする。
github.com


今後、自分で最適化アルゴリズムを実装するときに活かせることを知った気がする。
まあ、最適化アルゴリズムで並列化できそうなのは行列演算くらいしかなくて自分でやることはない気もするけど。