R:Профилирование кода — различия между версиями
м (→Пакет proftools) |
м (→Пакет profr) |
||
Строка 149: | Строка 149: | ||
<syntaxhighlight lang="rsplus"> | <syntaxhighlight lang="rsplus"> | ||
− | library(profr) | + | > library(profr) |
− | prof <- profr(invisible(desc( | + | > prof <- profr(invisible(desc(x))) |
− | + | > print(prof) | |
− | print(prof) | + | Read 3 items |
+ | f level time start end leaf source | ||
+ | 8 desc 1 0.04 0.00 0.04 FALSE <NA> | ||
+ | 9 median 2 0.02 0.00 0.02 FALSE stats | ||
+ | 10 sd 2 0.02 0.02 0.04 FALSE stats | ||
+ | 11 median.default 3 0.02 0.00 0.02 FALSE stats | ||
+ | 12 var 3 0.02 0.02 0.04 FALSE stats | ||
+ | 13 mean 4 0.02 0.00 0.02 FALSE base | ||
+ | 14 .Call 4 0.02 0.02 0.04 TRUE base | ||
+ | 15 sort 5 0.02 0.00 0.02 FALSE base | ||
+ | 16 sort.default 6 0.02 0.00 0.02 FALSE base | ||
+ | 17 sort.int 7 0.02 0.00 0.02 TRUE base | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Содержание столбцов: | Содержание столбцов: | ||
− | + | * «f» --- название функции; | |
− | + | * «level» --- уровень в иерархии вызовов; | |
− | + | * «time» --- общее время выполнения функции; | |
− | + | * «start» --- время начала выполнения функции; | |
− | + | * «end» --- время окончания выполнения функции; | |
− | + | * «leaf» --- TRUE, если функция вызывается другими функциями; | |
− | + | * «source» --- название пакеты, содержащего данную функцию. | |
− | + | ||
− | + | ||
Графическое представление результатов профилирования осуществляется с помощью стандартной функции {{Inline-code|plot()|lang="rsplus"}}: | Графическое представление результатов профилирования осуществляется с помощью стандартной функции {{Inline-code|plot()|lang="rsplus"}}: | ||
<syntaxhighlight lang="rsplus"> | <syntaxhighlight lang="rsplus"> | ||
− | plot(prof) | + | > plot(prof) |
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | [[Файл:Profr-ggplot-desc.png|600px|center]] | ||
== Функция proftable() == | == Функция proftable() == |
Версия 15:49, 27 января 2014
Пакет base
Описанные выше функции Шаблон:Inline-code, Шаблон:Inline-code, Шаблон:Inline-code позволяют оценить общее время выполнения выражения и нивелировать возможные вариации за счет множества попыток, однако для более полной оценки и определения стратегии оптимизации кода необходимо также выявлять и «узкие» места в выполняемом коде. К подобным «узким» местам можно отнести те вызовы, которые занимают наибольшее количество времени или расходуют больше всего памяти (при профилирования расхода памяти). Существуют специальные пакеты, предназначенные для организации профилирования или для более наглядной демонстрации результатов, однако далее пойдет речь о функции Шаблон:Inline-code из пакета Шаблон:Inline-code.
Принцип работы функции Шаблон:Inline-code таков: с заданным интервалом (аргумент Шаблон:Inline-code) делаются «снимки» вызовов и записываются в файл. Следует помнить о том, что Шаблон:Inline-code делает «снимки» каждые 0.02 секунды (значение по умолчанию), поэтому если выражение выполняется быстрее, то Шаблон:Inline-code не сможет его отследить. В таком случае периодичность «снимков» можно задать вручную с помощью аргумента Шаблон:Inline-code. Также можно увеличить объём данных, чтобы код выполнялся дольше.
Ещё одной специфической особенностью профилирования является то, что при профилирования записываются абсолютно все вызовы. Например, если вы используется функцию Шаблон:Inline-code для организации повторного выполнения выражения, то вызовы Шаблон:Inline-code также будут записаны в лог, что может затруднить его анализ. Аналогичная ситуация будет происходить при вызове скрипта через функцию Шаблон:Inline-code
Обратим внимание, что при профилировании мы можем использовать многократное выполнение анализируемой функции для сбора более надёжной статистики вызовов.
В качестве примера ниже представлены шаги процесса профилирования функции, которая рассчитывает первичные описательные статистики (среднее, медиану, стандартное отклонение, минимум и максимум) по сгенерированным [math]10^6[/math] нормально распределённым значениям.
<syntaxhighlight lang="rsplus"> > x <- rnorm(10^6) </syntaxhighlight>
Объявим данную функцию:
<syntaxhighlight lang="rsplus"> desc <- function(x) {
n <- length(x) mean <- mean(x) median <- median(x) sd <- sd(x) min <- min(x) max <- max(x) return(c(n, mean, median, sd, min, max))
} </syntaxhighlight>
Шаг 1. Создание временного файла для записи лога профилирования (необязательно):
<syntaxhighlight lang="rsplus"> > tmp.log <- tempfile(pattern = "prof-", fileext = ".log") </syntaxhighlight>
Посмотреть путь и имя временного файла лога можно, введя имя переменной Шаблон:Inline-code:
<syntaxhighlight lang="rsplus"> > tmp.log [1] "/tmp/RtmpAvhCfO/prof-23b61d761805.log" </syntaxhighlight>
Шаг 2. Собственно процедура профилирования:
<syntaxhighlight lang="rsplus">
- Включаем профилирование
> Rprof(tmp.log)
- Выполняем код
> for (i in seq_len(100)) + invisible(desc(x))
- отключаем профилирование
> Rprof(NULL) </syntaxhighlight>
Шаг 3. Вывод результатов:
<syntaxhighlight lang="rsplus"> > summaryRprof(tmp.log) </syntaxhighlight>
Шаг 4. Удаляем временный файл:
<syntaxhighlight lang="rsplus"> > unlink(tmp.log) </syntaxhighlight>
Рассмотрим результаты профилирования функции Шаблон:Inline-code:
<syntaxhighlight lang="rsplus"> > summaryRprof(tmp.log) $by.self
self.time self.pct total.time total.pct
"sort.int" 2.54 52.70 3.04 63.07 "is.na" 0.70 14.52 0.70 14.52 ".Call" 0.44 9.13 0.44 9.13 "min" 0.38 7.88 0.38 7.88 "any" 0.32 6.64 0.32 6.64 "max" 0.26 5.39 0.26 5.39 "mean.default" 0.18 3.73 0.18 3.73
$by.total
total.time total.pct self.time self.pct
"desc" 4.82 100.00 0.00 0.00 "median" 3.56 73.86 0.00 0.00 "median.default" 3.56 73.86 0.00 0.00 "mean" 3.22 66.80 0.00 0.00 "sort.int" 3.04 63.07 2.54 52.70 "sort" 3.04 63.07 0.00 0.00 "sort.default" 3.04 63.07 0.00 0.00 "is.na" 0.70 14.52 0.70 14.52 ".Call" 0.44 9.13 0.44 9.13 "sd" 0.44 9.13 0.00 0.00 "var" 0.44 9.13 0.00 0.00 "min" 0.38 7.88 0.38 7.88 "any" 0.32 6.64 0.32 6.64 "max" 0.26 5.39 0.26 5.39 "mean.default" 0.18 3.73 0.18 3.73
$sample.interval [1] 0.02
$sampling.time [1] 4.82 </syntaxhighlight>
Результатом подобных операций является 2 таблицы, отсортированные по разным основаниям: по времени выполнения функции (Шаблон:Inline-code) и общему времени выполнения функции (Шаблон:Inline-code). В столбцах представлены абсолютные и относительные (в процентах) показатели времени выполнения.
При профилировании скриптов включение опции Шаблон:Inline-code проставляет номера строк в которых производился вызов, что позволяет проанализировать производительность всего кода:
<syntaxhighlight lang="rsplus"> > Rprof(tmp.log, line.profiling = TRUE) > source("/path/scritpt.R") > Rprof(NULL) > summaryRprof(tmp.log) > unlink(tmp.log) </syntaxhighlight>
Пакет proftools
Также может оказаться полезной функция Шаблон:Inline-code из пакета Шаблон:Inline-code. Данная функция выводит таблицу, сходную с таблицей «by.total» возвращаемую функцией Шаблон:Inline-code, в одну и сортирует их по общему времени выполнения в процентах или по времени выполнения функции в процентах, если указать Шаблон:Inline-code:
<syntaxhighlight lang="rsplus"> > library(proftools) > flatProfile(readProfileData(tmp.log))
total.pct total.time self.pct self.time
desc 100.00 4.82 0.00 0.00 median 73.86 3.56 0.00 0.00 median.default 73.86 3.56 0.00 0.00 mean 66.80 3.22 0.00 0.00 sort 63.07 3.04 0.00 0.00 sort.default 63.07 3.04 0.00 0.00 sort.int 63.07 3.04 52.70 2.54 is.na 14.52 0.70 14.52 0.70 .Call 9.13 0.44 9.13 0.44 sd 9.13 0.44 0.00 0.00 var 9.13 0.44 0.00 0.00 min 7.88 0.38 7.88 0.38 any 6.64 0.32 6.64 0.32 max 5.39 0.26 5.39 0.26 mean.default 3.73 0.18 3.73 0.18 </syntaxhighlight>
Функция интересна тем, что предоставляет более компактный вывод при той же информативности, что и функция Шаблон:Inline-code.
Пакет profr
Пакет Шаблон:Inline-code предоставляет несколько функций для упрощения процесса профилирования, а также позволяет графически представить результаты профилирования. Результаты работы функции Шаблон:Inline-code представляют собой таблицу, с хронологическим перечислением вызовов. Поэтому при использовании данной функции необходимо использовать однократное выполнение выражения. Вышеприведённый листинг по профилированию работы функции Шаблон:Inline-code будет выглядеть следующим образом:
<syntaxhighlight lang="rsplus"> > library(profr) > prof <- profr(invisible(desc(x))) > print(prof) Read 3 items
f level time start end leaf source
8 desc 1 0.04 0.00 0.04 FALSE <NA> 9 median 2 0.02 0.00 0.02 FALSE stats 10 sd 2 0.02 0.02 0.04 FALSE stats 11 median.default 3 0.02 0.00 0.02 FALSE stats 12 var 3 0.02 0.02 0.04 FALSE stats 13 mean 4 0.02 0.00 0.02 FALSE base 14 .Call 4 0.02 0.02 0.04 TRUE base 15 sort 5 0.02 0.00 0.02 FALSE base 16 sort.default 6 0.02 0.00 0.02 FALSE base 17 sort.int 7 0.02 0.00 0.02 TRUE base </syntaxhighlight>
Содержание столбцов:
- «f» --- название функции;
- «level» --- уровень в иерархии вызовов;
- «time» --- общее время выполнения функции;
- «start» --- время начала выполнения функции;
- «end» --- время окончания выполнения функции;
- «leaf» --- TRUE, если функция вызывается другими функциями;
- «source» --- название пакеты, содержащего данную функцию.
Графическое представление результатов профилирования осуществляется с помощью стандартной функции Шаблон:Inline-code:
<syntaxhighlight lang="rsplus"> > plot(prof) </syntaxhighlight>
Функция proftable()
Рассмотрим ещё один способ представления результатов профилирования --- функция Шаблон:Inline-code, написанная Noam Ross. Для импорта данной файла скрипта нам понадобятся пакеты Шаблон:Inline-code и Шаблон:Inline-code, т.к. штатная функция Шаблон:Inline-code не поддерживает протокол https. Итак, импортируем скрипт с функцией Шаблон:Inline-code:
<syntaxhighlight lang="rsplus"> library(devtools) library(digest) source_url("https://raw.github.com/noamross/noamtools/master/R/proftable.R") </syntaxhighlight>
Для корректной работы функции Шаблон:Inline-code необходимо провести профилирование с включённой опцией Шаблон:Inline-code. Для удобства последующего анализа результатов сохраним нашу функцию и её вызов в файл:
<syntaxhighlight lang="rsplus"> tmp.R <- tempfile(fileext = ".R") dump("desc", tmp.R) cat("for (i in seq_len(100))\n invisible(desc(nvec))",
file = tmp.R, fill = TRUE, append = TRUE)
</syntaxhighlight>
Процедура профилирования осуществляется описанным выше способом:
<syntaxhighlight lang="rsplus"> Rprof(tmp.log, line.profiling = TRUE) source(tmp.R) Rprof(NULL) unlink(tmp.R) unlink(tmp.log) </syntaxhighlight>
Для работы функции Шаблон:Inline-code также необходим пакет Шаблон:Inline-code.
<syntaxhighlight lang="rsplus"> tmp.log <- paste0(LOGDIR, "/desc-lineprof.log") library(plyr) proftable(tmp.log) </syntaxhighlight>
Разберём вывод функции Шаблон:Inline-code. В первом столбце отображается время выполнения того или иного вызова, во втором --- название вызываемой функции. Далее по цепочке приведена иерархия вызовов. Весь вывод отсортирован по времени выполнения вызовов по убыванию. Вывод наглядно демонстрирует наиболее медленные вызовы вплоть до самого низкого уровня иерархии.
Обратите внимание, что функция Шаблон:Inline-code имеет второй аргумент --- Шаблон:Inline-code, который отвечает за количество строк в выводе.
Выводы
Из примера выше мы можем сделать вывод, что наиболее медленной функцией из состава функции Шаблон:Inline-code является функция Шаблон:Inline-code, которая через некоторую последовательность вызовов вызывает функцию Шаблон:Inline-code. Именно функция Шаблон:Inline-code является наиболее «узким» местом всех расчётов.
Иногда, чтобы выяснить какой функции принадлежит тот или иной вызов, приходится анализировать содержимое функций. Чтобы увидеть содержимое функции, можно воспользоваться функцией Шаблон:Inline-code. Например:
<syntaxhighlight lang="rsplus"> getAnywhere(median.default) </syntaxhighlight>
Если искомая функция принадлежит какому-то пакету, то этот пакет должен быть предварительно загружен.