GVL の解説
スレッドに制限がある理由です。
「GVL の解説」はCoddyKit上の無料Ruby Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはRuby Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Ruby Academyコースには全4レッスンが含まれています。
GVLとは何ですか
GVL(グローバルVMロック。歴史的にはGIL)は、標準RubyインタープリターであるCRuby内部のロックです。
- 一度に1つのスレッドだけがRubyコードを実行できるようにします。
- インタープリターの内部状態が並行実行によって破壊されるのを防ぎます。
- 純粋なRubyのCPU処理は、複数のコアで真の並列実行にならないことを意味します。
GVLを理解すると、スレッドが役立つ場面と、役立たない場面が分かります。
なぜ存在するのか
CRubyの内部処理(オブジェクトの割り当て、ガベージコレクション、C拡張)のすべてが、スレッドセーフになるように作られているわけではありません。GVLはRubyの実行を直列化することで、それらを安全にします。
GVLをきれいに取り除くのは難しいため、CRubyでは維持されています。JRubyとTruffleRubyにはGVLがなく、複数のコアでスレッドを実行できます。
CPUバウンドな処理
CPUバウンドなタスク(数学計算、ソート、解析)では、CRubyのもとでスレッドを使っても処理は速くなりません。どの瞬間にもRubyコードを実行できるスレッドは1つだけなので、2つのCPUスレッドを使っても、おおむね1つのスレッドにオーバーヘッドが加わった状態になります。
def heavy
sum = 0
1_000_00.times { |i| sum += i }
sum
end
puts heavy
puts "CPU work does not parallelize under the GVL"I/OはGVLを解放する
重要なポイントは、ブロッキングI/OがGVLを解放することです。スレッドがスリープしたり、ファイルを読み取ったり、ソケットを待機したりすると、ロックを手放すため、別のスレッドがRubyを実行できます。
GVLがあっても、スレッドがI/Oバウンドな処理に適しているのはこのためです。
待機処理の重複
ここでは3つのスレッドがそれぞれスリープします。sleepはGVLを解放するため、待機時間が重なります。合計時間は0.6秒ではなく、およそ0.2秒になります。
start = Time.now
threads = (1..3).map do
Thread.new { sleep 0.2 }
end
threads.each(&:join)
elapsed = Time.now - start
puts "Elapsed roughly: " + elapsed.round(1).to_sC拡張はGVLを解放できる
適切に実装されたC拡張は、rb_thread_call_without_gvlを使って、時間のかかるネイティブ計算中にGVLを解放できます。これにより、Rubyのスレッドが処理を続けている間も、負荷の高いネイティブ処理を並列実行できます。
そのため、CRuby上でも真の並列性を実現できるライブラリがあります。
スレッドは切り替わり続ける
CPU処理であっても、GVLが他のスレッドを永遠に停止させるわけではありません。CRubyは定期的にスレッドを強制的に切り替え、それぞれに順番を与えます。この処理は並行(交互に実行)ですが、並列(同時に実行)ではありません。
results = []
mutex = Mutex.new
threads = (1..3).map do |n|
Thread.new do
mutex.synchronize { results << n }
end
end
threads.each(&:join)
puts "Collected: " + results.sort.join(", ")並行性と並列性
重要な用語が2つあります。
- 並行性:タスクが交互に実行されることで進行します。GVLはこれを可能にします。
- 並列性:異なるコア上でタスクがまったく同じ瞬間に実行されます。GVLはRubyコードでこれを妨げます。
スレッドは並行性を、Ractorは並列性を提供します。
GVLを回避する方法
CRubyでCPU処理に複数のコアを使うには、次の方法があります。
- 複数のプロセスを実行する(各プロセスが独自のGVLを持ちます)。
- プロセス内の並列実行にRactorを使う。
- GVLを解放するC拡張に処理を委譲する。
I/O処理では、通常のスレッドで十分なことが多いです。
プロセスはGVLを共有しない
各OSプロセスは、独自のGVLを持つ独自のRuby VMを実行します。フォークすると真の並列性を実現できますが、プロセス同士はメモリを共有せず、より多くのリソースを使用します。
puts "Main pid: " + Process.pid.to_s
puts "Each forked process has its own independent GVL"戦略の選び方
判断の目安:
- 待機が多い場合:スレッドを使います。
- CPU負荷が高く、コアを活用したい場合:プロセスまたはRactorを使います。
- 状態を共有する必要がある場合:
Mutexを使ったスレッドを選びます。ただし、GVLによる制限に注意してください。
GVLを理解しておくと、CPUスレッドの性能がコア数に比例して向上すると期待してしまう、典型的な誤りを防げます。
確認問題
GVLについての理解度を確認しましょう。
まとめ:GVL
グローバルVMロックについて押さえておきたいポイント:
- CRubyでは、一度に1つのスレッドだけがRubyコードを実行します。
- インタープリターの内部状態を保護します。
- ブロッキングI/Oによって解放されるため、待機処理を重ねて実行できます。
- CPUバウンドなRuby処理は、複数のコアにまたがって並列化されません。
- 真の並列性にはプロセスまたはRactorを使います。
次は、軽量な協調的並行性を実現するファイバーについて学びます。
よくある質問
「GVL の解説」レッスンは無料ですか?
はい。「GVL の解説」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Ruby Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Ruby Academyコースには全4レッスンが含まれています。
「GVL の解説」で何を学びますか?
スレッドに制限がある理由です。 ブラウザで直接実行するハンズオンコードでRuby Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Ruby Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのRuby Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「GVL の解説」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このRuby Academyレッスンでコードを書いて実行できますか?
はい。すべてのRuby Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。