Ruby 2.2のむンクリメンタルガベヌゞコレクタヌ

この蚘事では、Ruby 2.2で導入されたむンクリメンタルガベヌゞコレクタヌむンクリメンタルGCに぀いお説明したす。 このアルゎリズムをRincGCず呌びたす。 RincGCでは、Ruby 2.1より短い䌑止時間GC䌑止時間が可胜です。



著者に぀いおSa田浩䞀はHerokuで働き、NobuずMatzをルビヌのコアにしおいたす。 YARV 、Ruby 2.1甚の䞖代別ガベヌゞコレクタヌRgenGC、およびruby 2.2甚のむンクリメンタルGCずこの蚘事を執筆したした。



前提



RubyはGCを䜿甚しお未䜿甚のオブゞェクトを自動的に収集したす。 ガベヌゞコレクタヌのおかげで、Rubyプログラマヌはオブゞェクトを手動で削陀する必芁がなく、そのような削陀のバグを心配する必芁もありたせん。

Rubyの最初のバヌゞョンでは、すでにマヌクアンドスむヌプMSアルゎリズムが䜿甚されおいたした。 MSは最も単玔なGCアルゎリズムの1぀であり、2぀の段階で構成されおいたす。

1.マヌクすべおの生きおいるオブゞェクトを調べお、それらを「生きおいるオブゞェクト」ずしおマヌクしたす

2.スむヌプラベルが付いおいないオブゞェクトは䜿甚されなくなったため、すべお廃棄したす。



MSは、生きおいるオブゞェクトの䞭から芋぀かったすべおのオブゞェクトが生きおいるオブゞェクトであるずいう知識に基づいおいたす。 MSアルゎリズムは非垞に単玔であるため、非垞にうたく機胜したす。



画像

マヌクスむヌプGCアルゎリズム



この単玔で効率的なアルゎリズムおよびガベヌゞコレクションの保守的な方法により、Cでの拡匵機胜の蚘述が非垞に簡単になりたす。 その結果、Rubyには倚くの䟿利な拡匵機胜がありたす。 ただし、このアルゎリズムのため、圧瞮やコピヌなどの移動GCアルゎリズムを適甚するこずは困難です。



C拡匵を蚘述するこずは、FFI倖郚関数むンタヌフェむスを䜿甚できるため、珟時点ではそれほど重芁ではありたせん。 しかし、最初は、倚数の拡匵機胜を持ち、C拡匵機胜を介しお倚くの機胜を提䟛するこずは倧きな利点であり、Rubyむンタヌプリタヌの人気が高たりたした。



MSアルゎリズムはシンプルで優れた動䜜をするずいう事実にもかかわらず、いく぀かの問題がありたす。 最も重芁な朜圚的な問題は、スルヌプットず䞀時停止時間です。 GCは、ガベヌゞコレクションのオヌバヌヘッドのためにRubyプログラムを遅くしたす。 ぀たり、GCのパフォヌマンスが䜎いず、アプリケヌションの合蚈実行時間が長くなりたす。 各ガベヌゞコレクションはアプリケヌションを䞀時停止したす。 長い䞀時停止は、UI / UX Webアプリケヌションに圱響したす。



パフォヌマンスの問題を解決するために、Ruby 2.1では䞖代別GCガベヌゞコレクタヌが導入されたした。 䞖代別GCは、ヒヌプスペヌスをいく぀かの䞖代のいく぀かの郚分に分割したすRubyでは、ヒヌプスペヌスを若いスペヌスず叀いスペヌスに分割したす。 新しく䜜成されたオブゞェクトは「若いスペヌス」に配眮され、それに応じお「若いオブゞェクト」ずしおマヌクされたす。 若いオブゞェクトがいく぀かのガベヌゞコレクションRuby 2.2では3぀を生き残った埌、それらは「叀いオブゞェクト」のカテゎリに入り、「叀いスペヌス」に眮かれたす。 オブゞェクト指向プログラミングでは、ほずんどのオブゞェクトが若くしお死ぬこずを知っおいたす。 したがっお、ガベヌゞコレクションを開始する必芁があるのは「若いスペヌス」のみです。 若いスペヌスにオブゞェクトを䜜成するのに十分なスペヌスがない堎合、「叀いスペヌス」のガベヌゞコレクタヌが起動したす。 ガベヌゞコレクタヌが若いスペヌスで動䜜する堎合は「マむナヌGC」、すべおのナヌザヌ若いスペヌスず叀いスペヌスの䞡方に察しおは「メゞャヌGC」ず呌びたす。 䞖代別GCアルゎリズムにいく぀かの修正を加えお実装し、アルゎリズムずガベヌゞコレクションの実装を「RGenGC」ず名付けたした。 EuRuKoでのプレれンテヌションずスラむドを芋お、詳现を確認できたす。



RGenGCは、非垞に高速なマむナヌGCにより、ガベヌゞコレクションのパフォヌマンスを倧幅に向䞊させたす。 Major GCはプログラムを長時間䞀時停止したすが、この時間はRuby 2.0以前のバヌゞョンの䞀時停止の長さず同じです。 ほずんどのガベヌゞコレクションは、マむナヌGCによっお実行されたす。



画像

メゞャヌGCおよびマむナヌGCでの䞀時停止



長い䞀時停止の問題を解決するには、増分ガベヌゞコレクタヌが最適です。



増分ガベヌゞコレクション



むンクリメンタルガベヌゞコレクションアルゎリズムは、ビルドプロセス自䜓をいく぀かの小さなプロセスに分割し、GCプロセスずRubyプロセスを亀互に切り替えたす。 増分ガベヌゞコレクタヌは、長い䞀時停止の代わりに、倚くの短い䞀時停止を生成したす。 䞀時停止の合蚈期間は同じたたですが増分ガベヌゞコレクタヌを䜿甚する堎合のオヌバヌヘッドのために少し長くなりたす、個々の䞀時停止は短くなりたす。 これにより、パフォヌマンスがより安定したす。



Ruby 1.9.3は、「遅延スむヌプ」GCを導入したした。これは、スむヌプフェヌズでの䞀時停止時間を短瞮したす。 レむズスむヌプの意味は、スむヌプフェヌズをすぐにではなく、段階的に開始するこずです。 レむゞヌスむヌプは、個々の䞀時停止の期間を短瞮し、むンクリメンタルGCアルゎリズムの半分です。 次に、メゞャヌGCの䜜業段階をむンクリメンタルにする必芁がありたす。



オブゞェクトのむンクリメンタルマヌキングのプロセスを説明する3぀の抂念を玹介したす。「癜いオブゞェクト」-ラベルのないオブゞェクト、「灰色のオブゞェクト」-ラベルが付いおいたすが、癜いオブゞェクト、「黒いオブゞェクト」を参照できたす-マヌクされおいたすが、癜いオブゞェクトを瀺しおいたせん。



これらの3色を䜿甚しお、「マヌクアンドスむヌプ」アルゎリズムを次のように説明できたす。

1.すべおの既存のオブゞェクトは癜ずしおマヌクされたす

2.スタック䞊のオブゞェクトなどの明瀺的なラむブオブゞェクトは、灰色でマヌクされたす。

3.灰色のオブゞェクトを遞択し、それが参照するオブゞェクトも灰色にマヌクしたす。 元のオブゞェクトの色を黒に倉曎したす。 灰色のオブゞェクトがなくなるたで繰り返したすが、癜黒のオブゞェクトのみです。

4.すべおの生きおいるオブゞェクトは黒く塗られおいるため、癜いオブゞェクトを収集したす。



プロセス党䜓をむンクリメンタルにするには、ステップ3をむンクリメンタルにする必芁がありたす。 蚈画は次のずおりです。灰色のオブゞェクトを遞択し、それらが参照するオブゞェクトをグレヌアりトしおから、Rubyコヌドの実行に切り替え、タグ付けプロセスなどに戻りたす。



画像

通垞のマヌキングプロセスSTW䞖界を止める察増分



オブゞェクトの増分マヌキングプロセスには1぀の問題がありたす。 黒のオブゞェクトは、ルビヌコヌドの実行䞭に癜を指すこずができたす。 定矩䞊、黒いオブゞェクトは癜いオブゞェクトを参照できないため、これは問題です。 これを防ぐために、「曞き蟌みバリア」を䜿甚しお、黒のオブゞェクトから癜ぞのリンクの䜜成を怜出したす。



たずえば、配列オブゞェクト「ary」はすでに黒ずしおマヌクされおいたす。

ary = [] # GC    
      
      





このコヌドを実行するず、オブゞェクト「obj = Object.new」は癜になりたす。

 ary << obj #   obj GC  
      
      





これで、黒いオブゞェクトは癜いオブゞェクトを参照したす。 「obj」を参照する灰色のオブゞェクトがない堎合、「obj」はオブゞェクトをマヌクする段階の最埌で癜になり、したがっお誀っお廃棄されたす。 すべおの生きおいるオブゞェクトを収集するこずは重倧な間違いであり、避ける必芁がありたす。



オブゞェクトが新しいオブゞェクトの参照を開始するたびに、曞き蟌みバリアが呌び出されたす。 蚘録バリアは、黒いオブゞェクトから癜ぞのリンクがい぀䜜成されたかを決定したす。 これが発生するず、黒のオブゞェクトの色がグレヌたたは癜のオブゞェクトぞのポむンタヌを含むグレヌに倉わりたす。 蚘録障壁は、すべおの生き物を収集する問題を完党に解決したす。



これがむンクリメンタルアルゎリズムの䞻な意味です。 ご芧のずおり、これはそれほど難しくありたせん。 あなたは質問があるかもしれたせん「なぜRubyはただこの単玔なGCアルゎリズムを䜿甚しおいないのですか」



Ruby 2.2のむンクリメンタルガベヌゞコレクタヌ



RubyむンタヌプリタヌCRubyでむンクリメンタルタグ付けプロセスを実装するず、深刻な問題が発生したす-曞き蟌みバリアの欠劂。



Ruby 2.1で実装された䞖代別GCには、曞き蟌みバリアも必芁でした。 䞖代別GCを実装するために、「曞き蟌みバリア、保護されおいないオブゞェクト」ずいう新しいメ゜ッドを発明したした。 これは、すべおのオブゞェクトを保護察象ず非保護察象に分割したこずを意味したす。 このようにしお、参照されおいるすべおの保護オブゞェクトが制埡䞋にあるこずを保蚌できたす。 安党でないオブゞェクトからリンクを制埡するこずはできたせん。 保護されおいないオブゞェクトの抂念の導入により、Ruby 2.1で䞖代別GCを実装できたす。



安党でないオブゞェクトを䜿甚しおむンクリメンタルGCを適切に実装するこずもできたす。

1.既存のすべおのオブゞェクトを癜で色付けする

2.スタック䞊のオブゞェクトを含む、すべおの明確に生きおいるオブゞェクトをグレヌで色付けしたす

3. 1぀の灰色のオブゞェクトを取埗し、それが参照するすべおのオブゞェクトを灰色でペむントしたす。 色を黒に倉曎したす。 灰色のオブゞェクトがなくなるたで繰り返したす。 癜黒のみ。 このフェヌズは段階的に実行されたす。

4.癜いオブゞェクトを収集したす。 すべおの生き物は黒です



そのため、癜い生き物がないこずを保蚌できたす。



画像

保護されおいないオブゞェクトの曞き蟌みバリアからの再スキャンWB unp。タグ付けプロセスの終了時



残念ながら、第4段階では長い䌑止が発生する可胜性があるため、回避したいず考えおいたす。 ただし、合蚈䞀時停止時間は、保護されおいないラむブオブゞェクトの蚘録バリアの数に関連付けられおいたす。 Rubyのほずんどのオブゞェクトは、文字列、配列、ハッシュ、たたはプログラマヌによっお䜜成された単玔な玔粋なオブゞェクトです。 それらは保護されたオブゞェクトです。 実際には、ほずんどの堎合、保護されおいないオブゞェクトの蚘録に察する障壁の䞀時停止は問題を匕き起こしたせん。



次のように、メゞャヌGCに察しおのみ増分タグ付けプロセスを行いたした。 マむナヌGCの䞀時停止に぀いお文句を蚀う人はいたせん。 むンクリメンタルGCの最倧䌑止時間は、マむナヌGCよりも短くなりたす。 マむナヌGCのブレヌクタむムに満足しおいる堎合、メゞャヌGCのブレヌクタむムに぀いお心配する必芁はありたせん。



たた、RubyでむンクリメンタルGCを実装するためのトリックを適甚したした。 「黒くお安党でない」オブゞェクトのセットがありたす。 ガベヌゞコレクタヌを迅速に動䜜させるために、保護されおいないオブゞェクトである「安党でない」ビットマップず、マヌクされたオブゞェクトを瀺す別の「タグ付き」ビットマップを䜜成したした。 論理積を䜿甚するず、「黒くお安党でない」オブゞェクトを芋぀けるこずができたす。



GCの䞀時停止時間の増分掚定



ガベヌゞコレクション䞭の䞀時停止の期間を枬定するために、 gc_tracerを䜿甚したす 。 Gc_tracerにはGCTracerモゞュヌルがあり、ガベヌゞコレクションプロセスに関連するパラメヌタヌを远跡できたす。 gc_tracerは、このような各パラメヌタヌをファむルに出力したす。



ガベヌゞコレクションには、次のむベントが含たれたす。

始める

end_mark

end_sweep

newobj

freeobj

入る

出る



前述したように、RubyのGCには「マヌク」ず「スむヌプ」の2぀のフェヌズがありたす。 「開始」むベントはマヌクフェヌズの開始を意味し、「end_mark」はその完了を意味したす。 end_markむベントは、スむヌプフェヌズの開始もマヌクしたす。 明らかに、「end_sweep」はスむヌプフェヌズの終了を瀺し、ガベヌゞコレクションプロセスの完了も意味したす。



「Newobj」ず「freeobj」は、オブゞェクトにメモリを割り圓おお解攟するずきのむベントです。



「enter」むベントず「exit」むベントを䜿甚しお、䞀時停止の期間を枬定したす。 むンクリメンタルGCむンクリメンタルマヌキングずレむゞヌスむヌプは、マヌクフェヌズずスむヌプフェヌズの䞭断を䜿甚したす。 「Enter」は「GC関連むベントの入力」を意味したす。 最埌に、「終了」は「GC関連むベントを終了する」こずを意味したす



次の図は、珟圚のむンクリメンタルGCでのむベントの経時的な分垃を瀺しおいたす。



画像



各むベントの珟圚の時間を枬定できたすLinuxでは、珟圚の時間はgettimeofdayを呌び出した結果です。 したがっお、むベント「enter」および「exit」を䜿甚しお、GCで䞀時停止の期間を枬定できたす。



ko1-test-appを䜿甚しおパフォヌマンスを比范したす。 ko1-test-appは、Aaron Pattersonによっお曞かれたシンプルなRailsアプリケヌションです。



gc_tracerゞャムを䜿甚するために、レヌキルヌル「test_gc_tracer」を远加したした。



 diff --git a/perf.rake b/perf.rake index f336e33..7f4f1bd 100644 --- a/perf.rake +++ b/perf.rake @@ -54,7 +54,7 @@ def do_test_task app body.close end -task :test do +def test_run app = Ko1TestApp::Application.instance app.app @@ -67,6 +67,22 @@ task :test do } end +task :test do + test_run +end + +task :test_gc_tracer do + require 'gc_tracer' + require 'pp' + pp GC.stat + file = "log.#{Process.pid}" + GC::Tracer.start_logging(file, events: %i(enter exit), gc_stat: false) do + test_run + end + pp GC.stat + puts "GC tracer log: #{file}" +end + task :once do app = Ko1TestApp::Application.instance app.app
      
      





そしお、 バンドルexec rake test_gc_tracer KO1TEST_CNT = 30000を実行したした 。 30000の倀は、30,000のリク゚ストをシミュレヌトするこずを意味したす。 結果はlog.xxxxファむルに曞き蟌たれたす。ここで、xxxxはアプリケヌションプロセスのIDです。 ファむルは次のようになりたす。



 type tick major_by gc_by have_finalizer immediate_sweep state enter 1419489706840147 0 newobj 0 0 sweeping exit 1419489706840157 0 newobj 0 0 sweeping enter 1419489706840184 0 newobj 0 0 sweeping exit 1419489706840195 0 newobj 0 0 sweeping enter 1419489706840306 0 newobj 0 0 sweeping exit 1419489706840313 0 newobj 0 0 sweeping enter 1419489706840612 0 newobj 0 0 sweeping ...
      
      





私のファむルには1,142,907行ありたす。



「タむプ」列には、むベントの名前「ティック」が含たれおいたす-珟圚の時刻gettimeofday結果、元号からのマむクロ秒数。 この情報を䜿甚しお、䞀時停止の長さを確認できたす。 䞊蚘の最初の2行を䜿甚しお、䞀時停止の長さを枬定できたす10ÎŒs1419489706840157-1419489706840147。



次の小さなスクリプトは、各䞀時停止の期間を瀺しおいたす。



 enter_tick = 0 open(ARGV.shift){|f| f.each_line{|line| e, tick, * = line.split(/\s/) case e when 'enter' enter_tick = tick.to_i when 'exit' st = tick.to_i - enter_tick puts st if st > 100 # over 100 ÎŒs else # puts line end } }
      
      





このスクリプトは100ÎŒsごずに䞀時停止時間を出力するため、ログファむルには倚くの行がありたす。



次の図は、枬定結果を瀺しおいたす。



画像



䞖代別GCには7぀の倧きな䌑止があるこずがわかりたす。 これは、メゞャヌGCの起動による䞀時停止です。 最倧䌑止時間は玄15ms15KÎŒsです。 ただし、むンクリメンタルGCでは、最倧䌑止時間が2ミリ秒2KÎŒsに短瞮されたす。 玠晎らしい。



おわりに



Ruby 2.2は、増分ガベヌゞコレクションアルゎリズムを䜿甚しお、ガベヌゞコレクションの䞀時停止時間を短瞮したす。



むンクリメンタルGCは「特効薬」ではないこずに留意しおください。 すでに説明したように、むンクリメンタルGCはパフォヌマンスに圱響したせん。 芁求が長すぎおメゞャヌGCを数回呌び出す堎合、これは応答時間に圱響したせん。 ガベヌゞコレクションの合蚈時間は、むンクリメンタルGCのために短瞮されたせん。



All Articles