GVL in Ruby
Rubyのスケール時にGVLの特性を効果的に活用する(翻訳)
GIL? GVL?
Ruby 1.9でYARVによってCRubyの内部構造が変わり、インタプリタではなく仮想マシン上にロックが存在するようになったのでGVLが正しい
コードを一回だけ解釈し、VMインストラクションに変換、そして実行する
インストラクションシーケンスを見てみる
code:shell
$ ruby --dump=insns -e "puts 1 + 1"
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,10)> (catch: FALSE)
0000 putself ( 1)Li
0001 putobject_INT2FIX_1_
0002 putobject_INT2FIX_1_
0003 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>
0005 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0007 leave
複数のスレッドがRuby VMに同時にアクセスするのは安全ではない
VMにグローバルロックをかけて一度に1つのスレッドだけがパラレルにアクセスできるようにしている
ユーザーの書くRubyプログラムで行われる作業の多くは、Ruby VMにアクセスする必要がない
大事なのはI/O待ち
待っている間GVLは解放され、戻ってきたときに獲得を試みる
スレッド数がN倍になっても、この部分がパフォーマンスに影響を及ぼす
アムダールの法則
パラレリズムの追加による速度向上は、パラレルに実行される時間の比率に関連している
1 / (1 - p + p/s)
pはタスクがパラレルに実行される割合(パーセンテージ)
sはタスクのうちリソースを増やした部分(パラレルになる部分)によって得られる高速化の係数
GVLがあっても、アプリケーションにスレッドを追加すれば、プロセスあたりのスループットが向上し、ひいてはメモリ消費も低減される
GitLabはWebサーバーをUnicorn(シングルスレッドモデル)からPuma(マルチスレッドモデル)に切り替え、メモリ使用量を30%削減
2025-02
So You Want To Remove The GVL?
GVLはスレッドセーフ性を保証するが完全な並列処理を妨げる
削除を求める声があるが大きな技術的課題が伴うため現時点ではGVL削除はコストに見合わないとされ、Ruby開発者間でも慎重な姿勢が取られている
GVLを削除すると、C言語で実装されたRubyの内部処理や拡張ライブラリに多数のロック操作を追加する必要がある
これはパフォーマンス低下やデバッグ負担増大を引き起こす
PythonのGIL削除の取り組みと比較すると、RubyはGCやオブジェクト操作の特性上、さらに多くの課題が予想される
GVL削除よりもスケジューラ改善やGVLを解放した状態での処理拡大など、現実的な改善策が優先されるべき