0Pricing
R Academy · Lekcja

Profilowanie kodu za pomocą Rprof i profvis

Zidentyfikuj funkcje, które zajmują najwięcej czasu w skryptach.

Profilowanie kodu za pomocą Rprof i profvis to bezpłatna lekcja R Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.

Czym jest profilowanie?

Pomiar czasu informuje, jak długo działa kod. Profilowanie pokazuje, gdzie w tym kodzie ten czas jest zużywany. Profiler R próbuje próbki stosu wywołań w regularnych odstępach, aby utworzyć statystyczny obraz funkcji, które są kosztowne obliczeniowo.

Dwa główne narzędzia to wbudowane Rprof() oraz interaktywny pakiet profvis.

Uruchamianie i zatrzymywanie Rprof()

Rprof('output.prof', interval = 0.01) uruchamia profiler. Zapisuje próbki stosu wywołań do pliku co 10 ms. Po wykonaniu wolnego kodu należy wywołać Rprof(NULL), aby zatrzymać rejestrowanie.

Argument interval określa częstotliwość próbkowania w sekundach — mniejsze wartości zapewniają większą rozdzielczość, ale tworzą większe pliki wyjściowe.

# 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

Odczytywanie wyników za pomocą summaryRprof()

summaryRprof('output.prof') analizuje plik profilowania i zwraca listę z dwiema ramkami danych:

  • by.self — czas spędzony w samej funkcji, z wyłączeniem wywoływanych przez nią funkcji
  • by.total — całkowity czas, w tym czas wszystkich funkcji wywołanych przez daną funkcję

Należy sortować według self.pct, aby znaleźć gorące punkty.

# 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

Czas własny a całkowity

Zrozumienie różnicy między czasem własnym a całkowitym ma kluczowe znaczenie podczas profilowania:

  • Czas własny — czas, przez który funkcja wykonywała własne wiersze kodu, nie czekając na wywoływane funkcje
  • Czas całkowity — czas własny powiększony o czas wszystkich wywołanych funkcji

Funkcja z wysokim czasem całkowitym, ale niskim czasem własnym jest wolna z powodu tego, co wywołuje, a nie z powodu własnej logiki. Należy optymalizować funkcję wywoływaną, a nie wywołującą.

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

Wprowadzenie do profvis

profvis opakowuje Rprof() i udostępnia interaktywny wykres płomieniowy HTML w RStudio lub przeglądarce. Jest on znacznie łatwiejszy do odczytania niż surowe wyniki summaryRprof().

Pakiet należy zainstalować raz za pomocą install.packages('profvis'), a następnie opakować kod wywołaniem 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)
# })

Odczytywanie wykresu płomieniowego profvis

Wynik profvis zawiera dwa panele:

  • Wykres płomieniowy — poziome paski, których szerokość oznacza czas; zagnieżdżone paski pokazują głębokość stosu wywołań
  • Tabela danych — sortowalny widok czasu własnego i całkowitego dla każdej funkcji oraz wiersza źródłowego

Szerokie paski u dołu wykresu płomieniowego oznaczają najbardziej kosztowne funkcje wywołujące. Wysokie stosy wskazują głębokie łańcuchy wywołań.

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

Identyfikowanie gorących punktów

Po obejrzeniu wyników profvis należy zidentyfikować gorące punkty, szukając funkcji, które są szerokie na wykresie płomieniowym i mają długi czas własny. Typowe przyczyny spowolnień w R:

  • Pętle niewektoryzowane wykonujące operacje element po elemencie
  • Powtarzane wywołania rbind() lub c(), które powiększają obiekty w pętli
  • Powtarzane wyrażenia regularne lub parsowanie ciągów znaków w wąskich pętlach
# 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
')

Profilowanie alokacji pamięci

Rprof może również śledzić alokacje pamięci za pomocą memory.profiling = TRUE. profvis pokazuje panel pamięci obok informacji o czasie, co pozwala wykryć funkcje alokujące duże obiekty tymczasowe — jedno z głównych źródeł przerw na 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
')

Profilowanie rzeczywistego potoku

Profilowanie potoku danych należy stosować systematycznie: opakować cały potok w profvis({}), zidentyfikować najwolniejszy etap, zoptymalizować go, a następnie ponownie wykonać profilowanie, aby potwierdzić poprawę. Nigdy nie należy optymalizować na ślepo.

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

Ograniczenia próbkowania Rprof

Rprof korzysta z próbkowania statystycznego, dlatego bardzo szybkie funkcje, działające krócej niż odstęp próbkowania, mogą się nie pojawić. Do mikrotestów porównawczych krótkich wyrażeń należy zamiast tego użyć pakietu microbenchmark.

Ponadto Rprof nie profiluje kodu C/C++ znajdującego się poniżej interfejsu R — widoczne są tylko stosy wywołań na poziomie 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
')

Najlepsze praktyki profilowania w R

Aby uzyskać wiarygodne wyniki profilowania, należy stosować następujące zasady:

  • Profilować realistyczne rozmiary danych — małe dane wejściowe ukrywają rzeczywiste wąskie gardło
  • Wykonać iteracje rozgrzewające przed profilowaniem, aby wykluczyć jednorazowy koszt konfiguracji
  • Profilować w czystej sesji R, aby uniknąć zakłóceń powodowanych przez załadowane pakiety
  • Używać profvis do eksploracji, a summaryRprof do raportów CI i raportów automatycznych
# 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
')

Szybkie sprawdzenie: czas własny a całkowity w Rprof

Funkcja ma wartości total.pct = 95% oraz self.pct = 3% w wynikach summaryRprof(). Co to oznacza?

Podsumowanie narzędzi profilowania

R udostępnia dwupoziomowy zestaw narzędzi do profilowania:

  • Rprof('file.prof', interval=0.01) + Rprof(NULL) + summaryRprof() — wbudowane, skryptowalne i przyjazne dla CI
  • profvis({...}) — interaktywny wykres płomieniowy z adnotacjami wierszy źródłowych i śledzeniem pamięci

Prawidłowy schemat postępowania zawsze wygląda tak: najpierw zmierzyć, zidentyfikować najgorętszy punkt, zoptymalizować tylko jego, a następnie ponownie zmierzyć, aby potwierdzić poprawę.

Często zadawane pytania

Czy lekcja „Profilowanie kodu za pomocą Rprof i profvis” jest bezpłatna?

Tak — pełny tekst „Profilowanie kodu za pomocą Rprof i profvis” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Profilowanie kodu za pomocą Rprof i profvis”?

Zidentyfikuj funkcje, które zajmują najwięcej czasu w skryptach. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć R Academy?

Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Profilowanie kodu za pomocą Rprof i profvis”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?

Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. system.time() i proc.time()
  2. Profilowanie kodu za pomocą Rprof i profvis
  3. Wektoryzacja na potrzeby wydajności
  4. Benchmarking za pomocą microbenchmark
← Powrót do R Academy