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 profilingsummaryRprof()で結果を読み取る
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.0Self時間と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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- system.time() と proc.time()
- Rprof と profvis でコードをプロファイリングする
- 高速化のためのベクトル化
- microbenchmark でベンチマークを行う