Изменения

R:Профилирование кода

589 байтов убрано, 13:26, 17 февраля 2014
м
Нет описания правки
В качестве примера ниже представлены шаги процесса профилирования функции, которая рассчитывает первичные описательные статистики (среднее, медиану, стандартное отклонение, минимум и максимум) по сгенерированным <math>10^6</math> нормально распределённым значениям.
<syntaxhighlight lang{{r-code|code="rsplus">
> x <- rnorm(10^6)
</syntaxhighlight>}}
Объявим данную функцию:
<syntaxhighlight lang{{r-code|code="rsplus">
desc <- function(x) {
n <- length(x)
return(c(n, mean, median, sd, min, max))
}
</syntaxhighlight>}}
Шаг 1. Создание временного файла для записи лога профилирования (необязательно):
<syntaxhighlight lang{{r-code|code="rsplus">
> tmp.log <- tempfile(pattern = "prof-", fileext = ".log")
</syntaxhighlight>}}
Посмотреть путь и имя временного файла лога можно, введя имя переменной <code>tmp.log</code>:
<syntaxhighlight lang{{r-code|code="rsplus">
> tmp.log
[1] "/tmp/RtmpAvhCfO/prof-23b61d761805.log"
</syntaxhighlight>}}
Шаг 2. Собственно процедура профилирования:
<syntaxhighlight lang{{r-code|code="rsplus">
# Включаем профилирование
> Rprof(tmp.log)
# отключаем профилирование
> Rprof(NULL)
</syntaxhighlight>}}
Шаг 3. Вывод результатов:
<syntaxhighlight lang{{r-code|code="rsplus">
> summaryRprof(tmp.log)
</syntaxhighlight>}}
Шаг 4. Удаляем временный файл:
<syntaxhighlight lang{{r-code|code="rsplus">
> unlink(tmp.log)
</syntaxhighlight>}}
Рассмотрим результаты профилирования функции <code>desc()</code>:
<syntaxhighlight lang{{r-code|code="rsplus">
> summaryRprof(tmp.log)
$by.self
$sampling.time
[1] 4.82
</syntaxhighlight>}}
Результатом подобных операций является 2 таблицы, отсортированные по разным основаниям: по времени выполнения функции (<code>self.time</code>) и общему времени выполнения функции (<code>total.time</code>). В столбцах представлены абсолютные и относительные (в процентах) показатели времени выполнения.
При профилировании скриптов включение опции <code>line.profiling</code> проставляет номера строк в которых производился вызов, что позволяет проанализировать производительность всего кода:
<syntaxhighlight lang{{r-code|code="rsplus">
> Rprof(tmp.log, line.profiling = TRUE)
> source("/path/scritpt.R")
> summaryRprof(tmp.log)
> unlink(tmp.log)
</syntaxhighlight>}}
== Пакет <code>proftools</code> ==
Также может оказаться полезной функция <code>flatProfile()</code> из пакета <code>proftools</code>. Данная функция выводит таблицу, сходную с таблицей «by.total» возвращаемую функцией <code>summaryRprof()</code>, в одну и сортирует их по общему времени выполнения в процентах или по времени выполнения функции в процентах, если указать <code>byTotal = FALSE</code>:
<syntaxhighlight lang{{r-code|code="rsplus">
> library(proftools)
> flatProfile(readProfileData(tmp.log))
max 5.39 0.26 5.39 0.26
mean.default 3.73 0.18 3.73 0.18
</syntaxhighlight>}}
Функция интересна тем, что предоставляет более компактный вывод при той же информативности, что и функция <code>summaryRprof()</code>.
Пакет <code>profr</code> предоставляет несколько функций для упрощения процесса профилирования, а также позволяет графически представить результаты профилирования. Результаты работы функции <code>profr</code> представляют собой таблицу, с хронологическим перечислением вызовов. Поэтому при использовании данной функции необходимо использовать однократное выполнение выражения. Вышеприведённый листинг по профилированию работы функции <code>desc()</code> будет выглядеть следующим образом:
<syntaxhighlight lang{{r-code|code="rsplus">
> library(profr)
> prof <- profr(desc(x))
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>}}
Содержание столбцов:
Графическое представление результатов профилирования осуществляется с помощью стандартной функции <code>plot()</code> или <code>ggplot()</code>:
<syntaxhighlight lang{{r-code|code="rsplus">
> ggplot(prof)
</syntaxhighlight>}}
[[Файл:Profr-ggplot-desc.png|600px|center]]
Мы переработали данную функцию, удалив зависимость от пакета <code>plyr</code> и существенно ускорив её. Код модифицированного варианта доступен в [https://bitbucket.org/psylab/r-scripts/src/master/proftable.R git-репозитории]. Импортировать данный скрипт можно с помощью функции <code>source_url()</code>, которая входит в состав пакета <code>devtools</code><ref>Штатная функция <code>source()</code> из пакета <code>base</code> не поддерживает загрузку файлов по протоколу https.</ref>:
<syntaxhighlight lang{{r-code|code="rsplus">
> library(devtools)
> source_url("https://bitbucket.org/psylab/r-scripts/raw/master/proftable.R")
</syntaxhighlight>}}
Для корректной работы функции <code>proftable()</code> необходимо провести профилирование с включённой опцией <code>line.profiling</code>. Для удобства последующего анализа результатов можно сохранить функцию и её вызов в файл скрипта и вызывать его с помощью функции <code>source()</code>. Скрипт для профилирования имеет следующее содержимое:
<syntaxhighlight {{file|lang="rsplus" line>|code=
desc <- function(x) {
n <- length(x)
for (i in seq_len(100)) desc(x)
</syntaxhighlight>}}
Процедура профилирования осуществляется описанным выше способом:
<syntaxhighlight lang{{r-code|code="rsplus">
> Rprof(tmp.log, line.profiling = TRUE)
> source("/tmp/script.R")
> Rprof(NULL)
</syntaxhighlight>}}
Результаты работы функции <code>proftable()</code> представлены ниже.
<syntaxhighlight lang{{r-code|code="rsplus">
> proftable(tmp.log)
PctTime Call
Total Time: 4.48 seconds
Percent of run time represented: 100%
</syntaxhighlight>}}
Разберём вывод функции <code>proftable()</code>. В первом столбце отображается время выполнения того или иного вызова, во втором --- название вызываемой функции. Далее по цепочке приведена иерархия вызовов. Весь вывод отсортирован по времени выполнения вызовов по убыванию. Вывод наглядно демонстрирует наиболее медленные вызовы вплоть до самого низкого уровня иерархии.
Иногда, чтобы выяснить какой функции принадлежит тот или иной вызов, приходится анализировать содержимое функций. Чтобы увидеть содержимое функции, можно воспользоваться функцией <code>getAnywhere()</code>. Например:
<syntaxhighlight lang{{r-code|code="rsplus">
getAnywhere(median.default)
A single object matching ‘median.default’ was found
<bytecode: 0x3c8f828>
<environment: namespace:stats>
</syntaxhighlight>}}
Если искомая функция принадлежит какому-то пакету, то этот пакет должен быть предварительно загружен.
С учётом выявленных «узких» мест нашей функции и проанализировав исходный код функций, мы можем оптимизировать нашу исходную функцию для достижения максимальной производительности. Наш вариант оптимизированной функции будет выглядеть следюущим образом:
<syntaxhighlight lang{{r-code|code="rsplus">
desc <- function(x) {
n <- length(x)
return(c(n, mean, median, sd, min, max))
}
</syntaxhighlight>}}
Вместо функций <code>median()</code> и <code>sd()</code> мы использовали общеизвестные формулы. А для расчёта медианы была взята часть оригинальной функции <code>median.default</code>, в которой вызов функций <code>sort()</code> был заменён на более быстрый вызов <code>.internal(psort)</code>.