0Pricing
R Academy · レッスン

Rprof と profvis でコードをプロファイリングする

スクリプト内で最も時間を消費している関数を特定します。

「Rprof と profvis でコードをプロファイリングする」はCoddyKit上の無料R Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

プロファイリングとは

時間測定では、コードがどれくらいの時間実行されるかがわかります。プロファイリングでは、そのコードのどこで時間が使われているかがわかります。Rのプロファイラーは一定間隔でコールスタックをサンプリングし、どの関数にコストがかかっているかを統計的に把握します。

主なツールは、組み込みのRprof()と、インタラクティブなprofvisパッケージです。

Rprof()の開始と停止

Rprof('output.prof', interval = 0.01)でプロファイラーを開始します。10ミリ秒ごとにコールスタックのサンプルをファイルへ書き込みます。遅いコードを実行したら、Rprof(NULL)を呼び出して記録を停止します。

interval引数は秒単位のサンプリング頻度を制御します。値を小さくすると解像度は上がりますが、出力ファイルは大きくなります。

# Pattern — do not run Rprof inside knitr/Quarto
# Rprof('my_profile.prof', interval = 0.01)
#
# slow_function <- function(n) {
#   x <- numeric(n)
#   for (i in seq_len(n)) x[i] <- sqrt(i)
#   sum(x)
# }
# slow_function(500000)
#
# Rprof(NULL)  # stop profiling

summaryRprof()で結果を読み取る

summaryRprof('output.prof')はプロファイリングファイルを解析し、2つのデータフレームを含むリストを返します:

  • by.self — 各関数自体にかかった時間(呼び出した関数を除く)
  • by.total — その関数が呼び出したすべての関数を含む合計時間

self.pctで並べ替えると、ホットスポットを見つけられます。

# After Rprof() run:
# prof <- summaryRprof('my_profile.prof')
# head(prof$by.self)
#
# Example output columns:
#            self.time self.pct total.time total.pct
# slow_fn        1.22     61.0       1.98      99.0
# sqrt           0.76     38.0       0.76      38.0
# sum            0.02      1.0       0.02       1.0

Self時間とTotal時間の違い

プロファイリングでは、Self時間とTotal時間の違いを理解することが重要です:

  • Self時間 — 関数自身の行の実行にかかった時間(呼び出した関数を待機した時間は含みません)
  • Total時間 — Self時間に、その関数が呼び出したすべての関数の実行時間を加えたもの

Total時間が長くSelf時間が短い関数は、自身のロジックではなく、呼び出している関数が原因で遅くなっています。呼び出し元ではなく、呼び出し先を最適化してください。

# Conceptual example:
# wrapper() -> process_data() -> slow_sort()
#
# total.pct: wrapper=100, process_data=90, slow_sort=85
# self.pct:  wrapper=5,   process_data=5,  slow_sort=85
#
# => slow_sort is the real bottleneck to fix.
cat('High total + low self => the culprit is a callee function
')

profvisの紹介

profvisはRprof()をラップし、RStudioまたはブラウザーでインタラクティブなHTMLフレームグラフを提供します。生のsummaryRprof()出力よりも、はるかに読みやすくなります。

install.packages('profvis')で一度インストールし、その後profvis({...})でコードをラップします。

# library(profvis)
#
# profvis({
#   n <- 200000
#   x <- numeric(n)
#   for (i in seq_len(n)) x[i] <- log(i)
#   total <- sum(x)
#   sorted <- sort(x)
# })

profvisのフレームグラフを読み取る

profvisの出力には2つのパネルがあります:

  • フレームグラフ — 横棒の幅が時間を表し、ネストされた棒がコールスタックの深さを表します
  • データテーブル — 関数とソースコードの行ごとのSelf時間とTotal時間を並べ替えて表示します

フレームグラフの下部にある幅の広い棒は、最もコストの高い呼び出し元です。スタックが高いほど、呼び出しの連鎖が深いことを示します。

# Reading the flame graph:
# - Each horizontal bar = one function on the call stack
# - Width proportional to time spent
# - Bottom = outermost caller, top = deepest callee
# - Click a bar to zoom in
# - 'Memory' tab shows allocation by line
cat('profvis shows self time per source line — invaluable for tight loops
')

ホットスポットを特定する

profvisの出力を確認したら、フレームグラフで幅が広く、Self時間が長い関数を探してホットスポットを特定します。Rでよくある原因は次のとおりです:

  • 要素ごとの処理を行うベクトル化されていないループ
  • ループ内でオブジェクトを大きくする、繰り返しのrbind()またはc()
  • タイトなループ内での正規表現または文字列解析の繰り返し
# Before fix — growing vector in loop (common hot spot)
# profvis reveals repeated reallocations:
# result <- c()
# for (i in 1:50000) result <- c(result, i^2)
#
# After fix — pre-allocated:
# result <- numeric(50000)
# for (i in 1:50000) result[i] <- i^2
cat('Pre-allocation eliminates the most common loop hot spot
')

メモリ割り当てをプロファイリングする

Rprofではmemory.profiling = TRUEを指定して、メモリ割り当ても追跡できます。profvisは時間測定と並べてメモリパネルを表示するため、大きな一時オブジェクトを割り当てる関数を見つけられます。これはガベージコレクションによる一時停止の主な原因です。

# Memory profiling with Rprof:
# Rprof('mem.prof', interval = 0.01, memory.profiling = TRUE)
# ... slow code ...
# Rprof(NULL)
# prof <- summaryRprof('mem.prof', memory = 'both')
# head(prof$by.self)
#
# profvis also shows mem delta per line automatically
cat('Memory profiling pinpoints allocation hot spots causing GC pauses
')

実際のパイプラインをプロファイリングする

データパイプラインには、体系的にプロファイリングを適用します。パイプライン全体をprofvis({})でラップし、最も遅い段階を特定して最適化した後、再度プロファイリングして改善を確認します。決して推測だけで最適化しないでください。

# Workflow:
# 1. profvis({ full_pipeline() })   => identify Stage 3 is 80% of time
# 2. Rewrite Stage 3 (vectorize / use data.table)
# 3. profvis({ full_pipeline() })   => confirm Stage 3 now < 10%
# 4. system.time({ full_pipeline() }) => confirm overall speedup
cat('Profile -> identify -> fix -> re-profile is the correct cycle
')

Rprofのサンプリングにおける制限

Rprofは統計的サンプリングを使用するため、非常に高速な関数(サンプリング間隔より短い関数)は表示されないことがあります。小さな式のマイクロベンチマークには、代わりにmicrobenchmarkパッケージを使ってください。

また、RprofはRインターフェースの下で実行されるC/C++コードをプロファイリングしません。確認できるのはRレベルのコールスタックだけです。

# Rprof interval = 0.01s => functions faster than 10ms may not appear
# For sub-millisecond work use microbenchmark:
#   microbenchmark(expr1, expr2, times = 1000L)
#
# For C-level profiling use external tools:
#   - Instruments (macOS)
#   - perf (Linux)
cat('Rprof is for R-level profiling; use microbenchmark for micro-timing
')

Rのプロファイリングにおけるベストプラクティス

信頼できるプロファイリング結果を得るには、次の方法に従ってください:

  • 現実的なデータサイズでプロファイリングします — 小さな入力では実際のボトルネックが隠れてしまいます
  • プロファイリング前にウォームアップ用の反復を実行し、1回限りのセットアップコストを除外します
  • 読み込んだパッケージの影響を避けるため、クリーンなRセッションでプロファイリングします
  • 調査にはprofvisを使い、CIや自動レポートにはsummaryRprofを使います
# Clean session profiling checklist:
# 1. Restart R (Ctrl+Shift+F10 in RStudio)
# 2. Load only required packages
# 3. Run once to warm up
# 4. profvis({ ... })  on second run
# 5. Compare before/after with system.time()
cat('Always profile with realistic data in a clean R session
')

クイックチェック:RprofのSelf時間とTotal時間

summaryRprof()の出力で、ある関数のtotal.pct = 95%に対してself.pct = 3%となっています。これは何を示していますか?

プロファイリングツールのまとめ

Rには、2段階のプロファイリングツールキットがあります:

  • Rprof('file.prof', interval=0.01) + Rprof(NULL) + summaryRprof() — 組み込みでスクリプトから実行でき、CIにも適しています
  • profvis({...}) — ソースコード行の注釈とメモリ追跡に対応した、インタラクティブなフレームグラフを提供します

正しいワークフローは常に、まず測定し、最も負荷の高い箇所を特定し、そこだけを最適化してから、再測定して効果を確認することです。

よくある質問

「Rprof と profvis でコードをプロファイリングする」レッスンは無料ですか?

はい。「Rprof と profvis でコードをプロファイリングする」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「Rprof と profvis でコードをプロファイリングする」で何を学びますか?

スクリプト内で最も時間を消費している関数を特定します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「Rprof と profvis でコードをプロファイリングする」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. system.time() と proc.time()
  2. Rprof と profvis でコードをプロファイリングする
  3. 高速化のためのベクトル化
  4. microbenchmark でベンチマークを行う
← R Academyに戻る