プロファイリング
パフォーマンスの問題を追跡しやすくするために、GitLabには一連のプロファイリングツールが用意されています。これらの中にはデフォルトで利用できるものもあれば、明示的に有効にする必要があるものもあります。
URL のプロファイリング
Gitlab::Profiler.profile
メソッドとそれに対応するbin/profile-url
スクリプトがあり、特定の URL に対する GET リクエストや POST リクエストを匿名ユーザー (デフォルト) あるいは特定のユーザーとしてプロファイリングすることができます。
プロファイラの最初の引数は、完全な URL (インスタンスホスト名を含む) あるいは絶対パス (先頭のスラッシュを含む) です。
デフォルトでは、レポート・ダンプは一時ファイルに保存され、Stackprof API を使用して操作できます。
スクリプトを使用する場合、引数を指定しないことで、コマンドライン・ドキュメントを使用できます。
対話的なコンソールセッションでこのメソッドを使用すると、そのコンソールセッション内でアプリケーションコードに加えられた変更がプロファイラの出力に反映されます。
使用例:
Gitlab::Profiler.profile('/my-user')
# Returns the location of the temp file where the report dump is stored
class UsersController; def show; sleep 100; end; end
Gitlab::Profiler.profile('/my-user')
# Returns the location of the temp file where the report dump is stored
# where 100 seconds is spent in UsersController#show
作成者を必要とするルートでは、ユーザをGitlab::Profiler
に提供する必要があります。 これは次のようにできます:
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)
Gitlab::Profiler.profile
にlogger:
キーワード引数を渡すと、ActiveRecord と ActionController のログ出力がそのロガーに送られます。その他のオプションはメソッドのソースに記載されています。
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, logger: Logger.new($stdout))
profiler_options
ハッシュを渡すと、サンプリングデータの出力ファイル (out
) を設定できます。例えば
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, profiler_options: { out: 'tmp/profile.dump' })
GitLab::Profiler
レポートの読み込み
サンプリング・データに対してStackprofを実行すると、時間が費やされた場所の要約を得ることができます。例えば
stackprof tmp/profile.dump
サンプリング・データの例:
==================================
Mode: wall(1000)
Samples: 8745 (6.92% miss rate)
GC: 1399 (16.00%)
==================================
TOTAL (pct) SAMPLES (pct) FRAME
1022 (11.7%) 1022 (11.7%) Sprockets::PathUtils#stat
957 (10.9%) 957 (10.9%) (marking)
493 (5.6%) 493 (5.6%) Sprockets::PathUtils#entries
576 (6.6%) 471 (5.4%) Mustermann::AST::Translator#decorator_for
439 (5.0%) 439 (5.0%) (sweeping)
630 (7.2%) 241 (2.8%) Sprockets::Cache::FileStore#get
208 (2.4%) 208 (2.4%) ActiveSupport::FileUpdateChecker#watched
206 (2.4%) 206 (2.4%) Digest::Instance#file
544 (6.2%) 176 (2.0%) Sprockets::Cache::FileStore#safe_open
176 (2.0%) 176 (2.0%) ActiveSupport::FileUpdateChecker#max_mtime
268 (3.1%) 147 (1.7%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
140 (1.6%) 140 (1.6%) ActiveSupport::BacktraceCleaner#add_gem_filter
116 (1.3%) 116 (1.3%) Bootsnap::CompileCache::ISeq.storage_to_output
160 (1.8%) 113 (1.3%) Gem::Version#<=>
109 (1.2%) 109 (1.2%) block in <main>
108 (1.2%) 108 (1.2%) Gem::Version.new
131 (1.5%) 105 (1.2%) Sprockets::EncodingUtils#unmarshaled_deflated
1166 (13.3%) 82 (0.9%) Mustermann::RegexpBased#initialize
82 (0.9%) 78 (0.9%) FileUtils.touch
72 (0.8%) 72 (0.8%) Sprockets::Manifest.compile_match_filter
71 (0.8%) 70 (0.8%) Grape::Router#compile!
91 (1.0%) 65 (0.7%) ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#query
93 (1.1%) 64 (0.7%) ActionDispatch::Journey::Path::Pattern::AnchoredRegexp#accept
59 (0.7%) 59 (0.7%) Mustermann::AST::Translator.dispatch_table
62 (0.7%) 59 (0.7%) Rails::BacktraceCleaner#initialize
2492 (28.5%) 49 (0.6%) Sprockets::PathUtils#stat_directory
242 (2.8%) 49 (0.6%) Gitlab::Instrumentation::RedisBase.add_call_details
47 (0.5%) 47 (0.5%) URI::RFC2396_Parser#escape
46 (0.5%) 46 (0.5%) #<Class:0x00000001090c2e70>#__setobj__
44 (0.5%) 44 (0.5%) Sprockets::Base#normalize_logical_path
フレームグラフも作成できます:
stackprof --d3-flamegraph tmp/profile.dump > flamegraph.html
詳細はStackprofのドキュメントを参照してください。
スピードスコープのフレームグラフ
パフォーマンスバーのフレームグラフサンプリングモードボタンを選択するか、performance_bar=flamegraph
パラメータをリクエストに追加することで、特定の URL のフレームグラフを生成できます。
ビューの詳細については、Speedscope のドキュメントを参照してください。
様々なサンプリング・モードについてはStackprofのドキュメントを参照してください。
これは、パフォーマンス・バーにアクセスできるすべてのユーザーに対して有効です。
弾丸
Bulletは、N+1クエリの問題を追跡するために使用できるGemです。クエリの問題をRailsログとブラウザコンソールに記録します。Bulletセクションはパフォーマンスバーに表示されます。
Bulletはデフォルトでは開発モードでのみ有効です。ただし、Bulletのロギングはノイズが多いため、ロギングは無効になっています。Bulletとそのロギングを設定するには:
-
環境上で手動でBulletを有効または無効にするには、
config/gitlab.yml
に以下の行を追加し、必要に応じてenabled
の値を変更してください:bullet: enabled: false
-
Bulletロギングを有効にするには、GitLabを起動する前に
ENABLE_BULLET
環境変数に空でない値を設定します:ENABLE_BULLET=true bundle exec rails s
Bullet を使ってN+1
クエリを見つけたときのフォローアップとして、リグレッションを防ぐためにQueryRecoder テストを書くことを検討してください。
システム統計
プロファイリング中やプロファイリング後に、メモリ消費量やCPUに費やされた時間、ガベージコレクタの統計情報など、Ruby仮想マシンのプロセスに関する詳細な情報を取得したくなることがあります。これらは様々なツールで個別に簡単に作成できますが、利便性のために、このデータを JSON ペイロードとしてエクスポートするサマリーエンドポイントが追加されました:
curl localhost:3000/-/metrics/system | jq
出力例です:
{
"version": "ruby 2.7.2p137 (2020-10-01 revision a8323b79eb) [x86_64-linux-gnu]",
"gc_stat": {
"count": 118,
"heap_allocated_pages": 11503,
"heap_sorted_length": 11503,
"heap_allocatable_pages": 0,
"heap_available_slots": 4688580,
"heap_live_slots": 3451712,
"heap_free_slots": 1236868,
"heap_final_slots": 0,
"heap_marked_slots": 3451450,
"heap_eden_pages": 11503,
"heap_tomb_pages": 0,
"total_allocated_pages": 11503,
"total_freed_pages": 0,
"total_allocated_objects": 32679478,
"total_freed_objects": 29227766,
"malloc_increase_bytes": 84760,
"malloc_increase_bytes_limit": 32883343,
"minor_gc_count": 88,
"major_gc_count": 30,
"compact_count": 0,
"remembered_wb_unprotected_objects": 114228,
"remembered_wb_unprotected_objects_limit": 228456,
"old_objects": 3185330,
"old_objects_limit": 6370660,
"oldmalloc_increase_bytes": 21838024,
"oldmalloc_increase_bytes_limit": 119181499
},
"memory_rss": 1326501888,
"memory_uss": 1048563712,
"memory_pss": 1139554304,
"time_cputime": 82.885264633,
"time_realtime": 1610459445.5579069,
"time_monotonic": 24001.23145713,
"worker_id": "puma_0"
}
パフォーマンスに影響する設定
アプリケーションの設定
-
development
環境はデフォルトでホットリロードを有効にして動作しますが、この場合Railsはリクエストごとにファイルの変更をチェックすることになり、ホットリロードがシングルスレッドであるため競合ロックが発生する可能性があります。 -
development
ホットリロードはシングルスレッドで行われるため、環境はリクエストが発生するとコードを遅延してロードします。
プロファイリング/ベンチマークのためにこれらの機能を無効にするには、GitLabを起動する前にRAILS_PROFILE
環境変数をtrue
に設定します。例えばGDKを使う場合:
- GDKのルートディレクトリに
env.runit
。 -
env.runit
ファイルにexport RAILS_PROFILE=true
を追加します。 - でGDKを再起動します。
gdk restart
この環境変数は開発モードにのみ適用されます。
GC設定
Rubyのガベージコレクタ(GC) は、アプリケーションのパフォーマンスに直接影響するさまざまな環境変数によって調整することができます。
以下の表に、これらの変数とデフォルト値を示します。
環境変数 | デフォルト値 | – | – | RUBY_GC_HEAP_INIT_SLOTS | 10000 | RUBY_GC_HEAP_FREE_SLOTS | 4096 | RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO | 0.20 | RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO | 0.40 | RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO | 0.65 | RUBY_GC_HEAP_GROWTH_FACTOR | 1.8 | RUBY_GC_HEAP_GROWTH_MAX_SLOTS | 0 (disable) | RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR | 2.0 | RUBY_GC_MALLOC_LIMIT(_MIN) | (16 * 1024 * 1024 /* 16MB */) | RUBY_GC_MALLOC_LIMIT_MAX | (32 * 1024 * 1024 /* 32MB */) | RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR | 1.4 | RUBY_GC_OLDMALLOC_LIMIT(_MIN) | (16 * 1024 * 1024 /* 16MB */) | RUBY_GC_OLDMALLOC_LIMIT_MAX | (128 * 1024 * 1024 /* 128MB */) | RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR | 1.2 | を参照してください。 |
(ソース)
GitLabは、アプリケーションのパフォーマンスを高速化したり、メモリ要件を下げたり、あるいはその両方を行うために、これらの設定を変更することがあります。
scripts/perf/gc/collect_gc_stats.rb
スクリプトを実行することで、これらの設定がアイドル状態のGitLabインスタンスのGCパフォーマンスやメモリ使用量、アプリケーション起動時間にどのような影響を与えるかを確認することができます。GCの統計と一般的なタイミングデータをCSVとして標準出力します。