このエントリーをはてなブックマークに追加

Ruby Garbage Collector Performance Tuning

Ruby のガベレージコレクションのパフォーマンスチューニングをする方法に環境変数を指定する方法があります。Ruby Enterprise Edition でも同様の設定が可能なので、パフォーマンスチューニングできるか試してみました。

以下、それぞれの環境変数の意訳です。

  • RUBY_HEAP_MIN_SLOTS: 最初のヒープスロット数です。デフォルトは、10000 です。
  • RUBY_HEAP_SLOTS_INCREMENT: Ruby が最初に新しいヒープを確保するときに必要な確保するために追加するヒープスロットの数です。デフォルトは、10000 です。例えば、デフォルトの GC 設定のとき、ヒープ上に 10000 の Ruby オブジェクトが存在しています。プログラムは他のオブジェクトを生成するとき、Ruby はそこに 10000 ヒープスロットで新しいヒープを確保するでしょう。合計で 20000 ヒープスロットがあると、10001 は使われて 9999 は使われていません。
  • RUBY_HEAP_SLOTS_GROWTH_FACTOR: マルチプリケーターが Ruby が新しいヒープスロットを必要としたときに次回に確保される新しいヒープスロットの数の計算に使われます。デフォルトは、1.8 です。プログラムを例にとると、プログラムは 10000 以上のオブジェクトを生成します。そして 10000 番目のオブジェクトは、Ruby は他のヒープを確保することが必要になります。このヒープは、10000 * 1.8 = 18000 ヒープスロットを持っているでしょう。合計で 20000 + 18000 = 38000 ヒープスロットがあるとき、20001 は使われて 17999 は使われません。次回 Ruby が新しいヒープを確保するとき、18000 * 1.8 = 32400 ヒープスロットはあるでしょう。
  • RUBY_GC_MALLOC_LIMIT: ガベレージコレクションのトリガーなしで確保することができる C データ構造体の量です。もしこの値が低すぎると、ガベレージコレクターは利用可能な空のヒープスロットさえ持って開始されます。デフォルト値は、8000000 です。
  • RUBY_HEAP_FREE_MIN: ガベレージコレクターが実行された後に利用可能であるべきヒープスロットの数です。もし、より少ないヒープスロットが利用可能なとき、Ruby は RUBY_HEAP_SLOTS_INCREMENT と RUBY_HEAP_SLOTS_GROWTH_FACTER パラメータにしたがって新しいヒープを確保します。デフォルト値は、4096 です。

アプリケーションからアプリケーションへベストな設定があります。さまざまに値を計測して実験するべきです。

37signals では、本番環境で次の値になっています。

RUBY_HEAP_MIN_SLOTS=600000
RUBY_GC_MALLOC_LIMIT=59000000
RUBY_HEAP_FREE_MIN=100000

Twitter では、本番環境で次の値になっています。

RUBY_HEAP_MIN_SLOTS=500000
RUBY_HEAP_SLOTS_INCREMENT=250000
RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
RUBY_GC_MALLOC_LIMIT=50000000

Twitter の設定は、次のことを意味しています。

  • アプリケーションを確保するための十分なメモリーを持って開始している
  • 必要な分だけリニアに増やす
  • ガベレージコレクターのみ、毎回 50 百万回 の malloc を呼んでいる

Twitter はこれらの設定を改善して、もっとも多くのメモリを使っているときのピーク時のメモリ使用量を、平均して 20% から 40% 程度のパフォーマンス向上することができた。

この設定を Passenger で変更するには、ここにあるとおり専用のラッパースクリプトを作って PassengerRuby に設定するだけです。

37signals と twitter の設定例をみてみると、多めのヒープスロットを確保している傾向にありますね。

Twitter の最新の設定は、Qcon の資料では次のようになっています。

RUBY_HEAP_MIN_SLOTS=500000
RUBY_HEAP_SLOTS_INCREMENT=250000
RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
RUBY_GP_MALLOC_LIMIT=50000000
RUBY_HEAP_FREE_MIN=4096

ということで調査はここまでにして、自分の Ruby アプリケーションをチューニングしていきたいと思います。