0Pricing
R Academy · Lezione

Profilazione del codice con Rprof e profvis

Individui quali funzioni richiedono più tempo nei Suoi script

Profilazione del codice con Rprof e profvis è una lezione R Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento R Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso R Academy include 4 lezioni in totale.

Che cos’è il profiling?

Il cronometraggio indica quanto tempo impiega il codice. Il profiling indica dove viene impiegato il tempo all’interno del codice. Il profiler di R campiona lo stack delle chiamate a intervalli regolari per costruire un quadro statistico delle funzioni più costose.

I due strumenti principali sono Rprof(), integrato, e il package interattivo profvis.

Avviare e arrestare Rprof()

Rprof('output.prof', interval = 0.01) avvia il profiler. Scrive in un file i campioni dello stack delle chiamate ogni 10 ms. Esegua il codice lento, quindi chiami Rprof(NULL) per interrompere la registrazione.

L’argomento interval controlla la frequenza di campionamento in secondi: valori più piccoli offrono una risoluzione maggiore, ma producono file più grandi.

# 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

Leggere i risultati con summaryRprof()

summaryRprof('output.prof') analizza il file di profiling e restituisce una lista con due data frame:

  • by.self — tempo trascorso direttamente in ciascuna funzione, escluse le funzioni chiamate
  • by.total — tempo totale, incluse tutte le funzioni chiamate da quella funzione

Ordini per self.pct per individuare i punti critici.

# 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

Tempo proprio e tempo totale

Comprendere la differenza tra tempo proprio e tempo totale è fondamentale per il profiling:

  • Tempo proprio — tempo impiegato dalla funzione per eseguire le proprie righe, senza attendere le funzioni chiamate
  • Tempo totale — tempo proprio più tutto il tempo impiegato dalle funzioni chiamate

Una funzione con tempo totale elevato ma tempo proprio ridotto è lenta a causa di ciò che chiama, non della propria logica. Ottimizzi la funzione chiamata, non quella chiamante.

# 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
')

Introduzione a profvis

profvis racchiude Rprof() e fornisce un flame graph HTML interattivo in RStudio o nel browser. È molto più semplice da leggere rispetto all’output grezzo di summaryRprof().

Lo installi una volta con install.packages('profvis'), quindi racchiuda il codice in 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)
# })

Leggere il flame graph di profvis

L’output di profvis presenta due pannelli:

  • Flame graph — barre orizzontali la cui larghezza indica il tempo; le barre annidate indicano la profondità dello stack delle chiamate
  • Tabella dei dati — vista ordinabile del tempo proprio e totale per funzione e riga sorgente

Le barre larghe nella parte inferiore del flame graph corrispondono alle funzioni chiamanti più costose. Gli stack alti indicano catene di chiamate profonde.

# 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
')

Individuare i punti critici

Dopo aver visualizzato l’output di profvis, individui i punti critici cercando funzioni ampie nel flame graph e con un tempo proprio elevato. Cause comuni in R:

  • Cicli non vettorializzati che eseguono operazioni elemento per elemento
  • rbind() o c() ripetuti che fanno crescere gli oggetti in un ciclo
  • Analisi di espressioni regolari o stringhe ripetuta in cicli intensivi
# 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
')

Profilare le allocazioni di memoria

Rprof può anche monitorare le allocazioni di memoria con memory.profiling = TRUE. profvis mostra un pannello della memoria accanto a quello dei tempi, consentendo di individuare le funzioni che allocano grandi oggetti temporanei, una causa importante delle pause del garbage collection.

# 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
')

Profilare una pipeline reale

Applichi il profiling sistematicamente a una pipeline di dati: racchiuda l’intera pipeline in profvis({}), individui la fase più lenta, la ottimizzi, quindi esegua nuovamente il profiling per confermare il miglioramento. Non ottimizzi mai alla cieca.

# 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
')

Limiti del campionamento di Rprof

Rprof utilizza un campionamento statistico, quindi le funzioni molto rapide, più veloci dell’intervallo di campionamento, potrebbero non comparire. Per i micro-benchmark di espressioni molto piccole, utilizzi invece il package microbenchmark.

Inoltre, Rprof non esegue il profiling del codice C/C++ sottostante l’interfaccia R: sono visibili solo gli stack delle chiamate a livello 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
')

Buone pratiche per il profiling in R

Segua queste pratiche per ottenere risultati affidabili dal profiling:

  • Esegua il profiling con dimensioni dei dati realistiche: input piccoli nascondono il vero collo di bottiglia
  • Esegua iterazioni di riscaldamento prima del profiling, per escludere i costi della configurazione iniziale
  • Esegua il profiling in una sessione R pulita, per evitare interferenze dei package caricati
  • Utilizzi profvis per l’esplorazione e summaryRprof per report CI/automatizzati
# 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
')

Verifica rapida: tempo proprio e totale in Rprof

Una funzione mostra total.pct = 95% ma self.pct = 3% nell’output di summaryRprof(). Che cosa indica questo risultato?

Riepilogo degli strumenti di profiling

R offre un toolkit di profiling a due livelli:

  • Rprof('file.prof', interval=0.01) + Rprof(NULL) + summaryRprof() — integrato, utilizzabile negli script e adatto alla CI
  • profvis({...}) — flame graph interattivo con annotazioni delle righe sorgente e monitoraggio della memoria

Il flusso di lavoro corretto è sempre: misurare prima, individuare il punto più critico, ottimizzare solo quello e misurare nuovamente per confermare il miglioramento.

Domande Frequenti

La lezione «Profilazione del codice con Rprof e profvis» è gratuita?

Sì — il testo completo di «Profilazione del codice con Rprof e profvis» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso R Academy, passa a CoddyKit PRO. Il corso R Academy include 4 lezioni in totale.

Cosa imparerò in «Profilazione del codice con Rprof e profvis»?

Individui quali funzioni richiedono più tempo nei Suoi script Eserciti R Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare R Academy?

Non è richiesta alcuna esperienza precedente. R Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Profilazione del codice con Rprof e profvis»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione R Academy?

Sì. Ogni lezione R Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. system.time() e proc.time()
  2. Profilazione del codice con Rprof e profvis
  3. Vettorizzazione per aumentare la velocità
  4. Benchmark con microbenchmark
← Torna a R Academy