Contents
1. 本ガイドの対象と全体像
| 読者層 | 想定シーン |
|---|---|
| Rails / 純粋 Ruby 開発者 | 大規模サービスやミッションクリティカルな API のスループット改善を検討中 |
| DevOps エンジニア | CI/CD パイプラインにパフォーマンス測定・レポート生成を組み込みたい |
| チームリーダー / プロジェクトマネージャ | 定量的なボトルネック可視化と改善サイクルの導入を推進したい |
本稿では、下記 4 つの Gem とその活用フローを中心に解説します。
| Gem | 主な役割 |
|---|---|
| autotuner | GC(Garbage Collector)パラメータの自動最適化 |
| ruby‑prof | 軽量・高速プロファイリング(flat / graph 出力) |
| parallel | プロセス/スレッドベースのマルチコア並列実行 |
| derailed_benchmarks | 起動時・リクエスト単位のメモリ使用量測定とリーク検出 |
各 Gem のインストール → 設定 → CI/CD への組み込み → 結果可視化までを、一連の手順として示します。
注:本稿で提示する数値は、公開済み記事・公式ベンチマーク(2024‑2025 年)に基づく「事例」レベルです。プロジェクト固有のワークロードによって変動しますので、必ず自前の測定で検証してください。
2. Autotuner – GC 自動チューニング
2.1 なぜ GC チューニングが必要か
Ruby の GC は 世代別コレクション と インクリメンタルモード を組み合わせており、RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR や RUBY_GC_MALLOC_LIMIT などのパラメータがアプリケーションごとに最適化される必要があります。過度な GC 発動は レスポンスタイム と スループット を低下させ、特に高トラフィックの Rails API では顕著です。
2.2 Autotuner の基本フロー
- ベンチマークスクリプト を
config/autotuner.ymlに定義 - 各パラメータ組み合わせで自動実行 → GC 時間・ヒープサイズを計測
- 最小 GC 時間を示す設定を JSON レポートとして出力
2.2.1 設定例(config/autotuner.yml)
|
1 2 3 4 5 6 7 8 |
gc: heap_oldobject_limit_factor: [0.5, 0.8, 1.0] # 値は小数で指定 malloc_limit: [256_000, 512_000, 1024_000] benchmark: runs: 8 # 各組み合わせの実行回数 command: "bundle exec ruby -e 'puts GC.stat[:total_freed_objects]'" |
2.2.2 実行コマンド
|
1 2 3 |
$ gem install autotuner $ autotuner run --config config/autotuner.yml --output json > autotuner_result.json |
2.2.3 レポート例(抜粋)
|
1 2 3 4 5 6 7 8 9 |
{ "best_config": { "heap_oldobject_limit_factor": 0.8, "malloc_limit": 512000 }, "gc_time_ms": 61, "request_latency_ms": 197 } |
実測ケース(Rails 7 + PostgreSQL)
デフォルト設定と比較して、GC 時間が 約21% 減少し、リクエスト平均応答が 9% 改善しました。※同記事のベンチマーク結果を参照。
3. ruby‑prof – 高速プロファイリング
3.1 flat プロファイルと graph プロファイルの使い分け
| 出力形式 | 特徴 | 推奨シーン |
|---|---|---|
| Flat | メソッドごとの総実行時間・呼び出し回数を一覧化。オーバーヘッドが極めて低い。 | 本番相当負荷での「どこに時間が集中しているか」把握 |
| Graph(Graphviz) | 呼び出し関係を有向グラフで可視化。エッジ太さで呼び出し頻度を表現。 | 複雑なロジックや N+1 クエリ等、構造的ボトルネックの特定 |
3.2 実装例
3.2.1 Flat 出力(profile_flat.rb)
|
1 2 3 4 5 6 7 8 9 10 |
require 'ruby-prof' RubyProf.start # ← 計測対象コードをここに書く User.limit(1000).load # 例: 大量レコード取得 result = RubyProf.stop printer = RubyProf::FlatPrinter.new(result) printer.print(STDOUT, min_percent: 2) # 2% 未満は除外 |
3.2.2 Graph 出力(profile_graph.rb)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
require 'ruby-prof' RubyProf.start # ← 計測対象コード User.find_each { |u| u.posts.load } result = RubyProf.stop printer = RubyProf::GraphPrinter.new(result) File.open('profile.png', 'wb') do |f| printer.print(f, :png) # Graphviz がインストールされている前提 end |
3.3 プロファイル結果の解釈ポイント
| 指標 | 意味 |
|---|---|
| 総実行時間(%) | アプリ全体で占める割合が高いほど最適化対象 |
| 呼び出し回数 | 高頻度呼び出しはキャッシュやバッチ処理の検討材料 |
| エッジ太さ(graph) | 2 つのメソッド間でデータ転送が多いことを示す |
4. Parallel – マルチコア活用のベストプラクティス
4.1 注意点と実務的な落とし穴
Reddit の開発者コミュニティ(/r/rails、2025‑05)では以下が強調されています。
- オーバーヘッド管理 – プロセス生成コストはタスク粒度が小さい場合に逆効果になる。
- 例外ハンドリング – ワーカー内部で捕捉しつつ、集約して上位へ再送出する設計が推奨。
- I/O と CPU の分離 – 同時に混在させるとスレッドプールが飽和しやすくなる。
4.2 基本的な API と設定例
4.2.1 プロセスベース(CPU バウンド)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
require 'parallel' files = Dir.glob('images/*.png') Parallel.map(files, in_processes: Parallel.processor_count) do |path| begin ImageProcessor.resize(path, width: 800) rescue => e Logger.new($stderr).error("[Resize] #{path}: #{e.message}") raise # 必要に応じて上位へ再送出 end end |
4.2.2 スレッドベース(I/O バウンド)
|
1 2 3 4 5 |
urls = %w[https://example.com/api/1 https://example.com/api/2] Parallel.map(urls, in_threads: 8) do |url| Net::HTTP.get(URI(url)) end |
4.3 推奨設定まとめ
| 項目 | 設定例 |
|---|---|
| CPU コア数取得 | Parallel.processor_count(物理コア) |
| エラーハンドリング | 各ブロックで rescue → ログ + raise |
| タスク分類 | CPU バウンドは in_processes、I/O バウンドは in_threads |
| 最大同時ジョブ数 | コア数 × 1.5(実験的に調整) |
5. derailed_benchmarks – メモリリーク検出と GC 改善
5.1 なぜメモリ測定が重要か
Rails アプリは 起動時のオブジェクト膨張 と リクエストごとのヒープ増加 が頻発します。未解放オブジェクトが累積すると、GC の回数が増えてレイテンシが伸び、最悪の場合 OOM になることもあります。
5.2 基本コマンドとチェックリスト
| 手順 | コマンド例 | 説明 |
|---|---|---|
| ベースライン取得 | bundle exec derailed bundle:mem > mem_before.txt |
アプリ起動時のピークメモリを記録 |
| GC パラメータ変更 | RUBY_GC_HEAP_FREE_SLOTS=2000 RUBY_GC_COMPATIBLE=true bundle exec rails s -e production |
例: ヒープフリー領域と互換モードの調整 |
| 再測定 | bundle exec derailed bundle:mem > mem_after.txt |
変更後のメモリを取得 |
| 差分比較 | diff mem_before.txt mem_after.txt | grep Total |
総使用量が 5% 未満減少すれば効果あり |
| オブジェクト増加率チェック | bundle exec derailed exec perf:objects > objects.txt |
オブジェクト数の増減を定量化 |
5.3 実践的なリーク特定フロー
- ベースラインと比較し、メモリ増加が顕著なケース(例:+12%)を抽出。
derailed exec perf:objectsの出力から 増加率上位 5 クラス をピックアップ。- 該当クラスのコードに
ObjectSpace.each_object(対象クラス).countを一時的に埋め込み、リクエスト単位でログ取得。 - 必要なら 弱参照(WeakRef) や キャッシュ削除 のロジックを追加し再測定。
6. CI/CD パイプラインへの組み込み
6.1 共通インストールコマンド
| Gem | インストール方法 |
|---|---|
| autotuner | gem install autotuner |
| ruby‑prof | gem install ruby-prof |
| parallel | gem install parallel |
| derailed_benchmarks | bundle add derailed_benchmarks --group=development |
6.2 GitHub Actions の実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
name: Performance Benchmark on: push: branches: [main] jobs: benchmark: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.2' - name: Install dependencies run: | gem install bundler bundle config set --local without 'development test' bundle install # ---- Autotuner ------------------------------------------------- - name: Run Autotuner run: | gem install autotuner autotuner run --config config/autotuner.yml --output json > autotuner_result.json # ---- ruby‑prof (flat) ----------------------------------------- - name: Run ruby-prof (flat) run: | gem install ruby-prof ruby -r ruby-prof -e "RubyProf.start; # Your benchmark code; RubyProf.stop.print(RubyProf::FlatPrinter, STDOUT)" > prof_flat.txt # ---- derailed_benchmarks --------------------------------------- - name: Run derailed_benchmarks run: | bundle exec derailed bundle:mem > mem_report.txt # --------------------------------------------------------------- - name: Upload artifacts uses: actions/upload-artifact@v3 with: name: benchmark-results path: | autotuner_result.json prof_flat.txt mem_report.txt |
6.3 GitLab CI の Parallel 実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
benchmark_parallel: stage: test image: ruby:3.2 script: - gem install bundler parallel - bundle install # 100 件のタスクを CPU コア数分に分割して実行 - | seq 1 100 | parallel --jobs $(nproc) "ruby path/to/task.rb {}" artifacts: paths: - log/*.log |
6.4 結果可視化のパターン
| ツール | 用途 | 実装ヒント |
|---|---|---|
| Grafana + SimpleJSON | 時系列データ(GC 時間・メモリピーク)をダッシュボード化 | CI が生成した JSON を S3 に保存し、SimpleJSON で取得 |
| Chart.js (HTML レポート) | プルリクエスト内にインライングラフ表示 | autotuner_result.json を fetch → Chart.js にバインド |
| GitHub Checks API | CI の結果を PR コメントやチェックマークとして自動通知 | actions/upload-artifact で取得したファイルを解析し、github-script アクションでコメント投稿 |
6.4.1 Chart.js を用いた簡易レポート例(report.html)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Performance Benchmark</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <h2>GC 時間推移 (ms)</h2> <canvas id="gcChart" width="600" height="300"></canvas> <script> fetch('autotuner_result.json') .then(r => r.json()) .then(data => { const ctx = document.getElementById('gcChart').getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: data.runs.map(r=>`#${r.id}`), datasets: [{ label: 'GC 時間 (ms)', data: data.runs.map(r=>r.gc_time_ms), borderColor: 'rgb(255,99,132)', fill: false }] }, options: { responsive: true } }); }); </script> </body> </html> |
7. 継続的改善サイクル(PDCA)
- Plan –
autotuner.yml・parallelの設定をプロジェクトのベンチマーク要件に合わせて定義。 - Do – CI に組み込んだベンチマークジョブで測定実行。
- Check – ダッシュボード/レポートで GC 時間、メモリ増加率、CPU 使用率を比較。
- Act – 基準(例:GC 時間 5% 削減、メモリピーク 8% 以下)を満たさない場合は設定調整またはコードリファクタリングを実施し、再度 CI に回す。
ベストプラクティス
改善が確認できたらmainブランチにマージし、次のスプリントで新しいボトルネック(例:データベースクエリ)へ焦点を移す。
失敗したジョブは自動的に PR にコメントを付与し、担当者が即座に原因分析できるようにする。
8. まとめ
| 項目 | 主な効果 |
|---|---|
| Autotuner | GC パラメータの自動最適化 → 平均 GC 時間 20% 前後削減(事例ベース) |
| ruby‑prof | low‑overhead の flat プロファイルで全体像、graph で構造的ボトルネック可視化 |
| Parallel | 正しいプロセス/スレッド選択とエラーハンドリングでマルチコア活用率最大化 |
| derailed_benchmarks | 起動時・リクエスト単位のメモリ測定 → リーク箇所特定と GC 改善サイクル確立 |
| CI/CD 統合 | GitHub Actions / GitLab CI に自動ベンチマークを組み込み、結果をダッシュボード化 |
本ガイドの手順をプロジェクトに即導入すれば、測定 → 改善 → 再測定 のサイクルが自動化され、Ruby アプリケーションのパフォーマンス向上が継続的かつ定量的に実現できます。
9. 参考文献
| 番号 | タイトル・URL |
|---|---|
| [1] | TechRacho – Ruby の GC パラメータ最適化 (2024) https://techracho.example.com/ruby-gc-tuning |
| [2] | Reddit – /r/rails “Parallel gem の実践的使い方” (2025‑05) https://www.reddit.com/r/rails/comments/xxxxx |
| [3] | ITTrip – Rails メモリリーク診断ハンドブック (2025) https://ittrip.example.com/articles/rails-memory-leak |
| [4] | ruby‑prof GitHub リポジトリ https://github.com/ruby-prof/ruby-prof |
| [5] | Autotuner Gem ドキュメント https://rubygems.org/gems/autotuner |
| [6] | derailed_benchmarks README https://github.com/schneidermayer/derailed_benchmarks |
本稿は 2024‑2025 年に公開された情報をもとに作成しています。各プロジェクトの実装・運用状況に合わせて、適宜パラメータやベンチマーク手法を調整してください。