R:Оптимизация/Компиляция в байт-код — различия между версиями

Материал Psylab.info - энциклопедии психодиагностики
Перейти к: навигация, поиск
м (Компиляция функций и выражений)
м (Компиляция функций и выражений)
Строка 79: Строка 79:
 
     la2c(x, is.null) 403.6 415.6  439.7  532.1 1361  100</nowiki>}}
 
     la2c(x, is.null) 403.6 415.6  439.7  532.1 1361  100</nowiki>}}
  
Обращается на себя внимание, что скомпилированная версия функции <code>lapply()</code> не превосходит по производительности оригинальную версию.
+
Обращается на себя внимание, что скомпилированная версия функции <code>lapply()</code> не превосходит по производительности оригинальную версию. Происходит это потому, что функция <code>lapply()</code> использует написанную на C, скомпилированную функцию. Также отметим, что скомпилированные версии функций <code>la1()</code> и <code>la2()</code> показываются практически одинаковую производительность, тогда как нескомпилированные версии довольно сильно различались. Это достигается за счёт высокой оптимизации работы циклов при компиляции функций.
  
 
== Компиляция скриптов ==
 
== Компиляция скриптов ==

Версия 13:20, 4 мая 2014


В данном материале рассматривается один из способов ускорения выполнения кода путём компиляции в байт-код. Байт-код - машинно-независимый код низкого уровня, генерируемый транслятором и исполняемый интерпретатором. Большинство инструкций байт-кода эквивалентны одной или нескольким командам ассемблера. Трансляция в байт-код занимает промежуточное положение между компиляцией в машинный код и интерпретацией.

Программа на байт-коде обычно выполняется интерпретатором байт-кода (обычно он называется виртуальной машиной, поскольку подобен компьютеру). Преимущество - в портируемости, т. е. один и тот же байт-код может исполняться на разных платформах и архитектурах. То же самое преимущество дают интерпретируемые языки. Однако, поскольку байт-код обычно менее абстрактный, более компактный и более «компьютерный», чем исходный код, эффективность байт-кода обычно выше, чем чистая интерпретация исходного кода, предназначенного для правки человеком.

В составе базовых пакетов R поставляется пакет compiler, который входит состав ядра R, но не загружается по умолчанию при старте R-сессии. Данный пакет включает в себя ряд функций для компиляции R-кода в байт-код.

Параметры компиляции

Все рассмотренные ранее функции из пакета compiler имеют опции, которые могут быть переданы в качестве аргументов функциям компиляции (аргумент options), или заданы глобально с помощью функции setCompilerOptions().

Рассмотрим эти опции:

  • optimize --- определяет уровень оптимизации: принимает значения от 0 до 3 (по умолчанию 2);
  • suppressAll --- управляет сообщениями: принимает значения TRUE или FALSE (по умолчанию code>FALSE</code>);
  • suppressUndefined --- управление сообщения о неопределённых (undefined) переменных: может принимать значения TRUE или список имён переменных (по умолчанию ".Generic", .Method", ".Random.seed", ".self").

Получить текущее значение глобальных опций компиляции можно с помощью функции getCompilerOption():

КодR

<syntaxhighlight lang="r">> getCompilerOption("optimize") [1] 2 > getCompilerOption("suppressAll") [1] FALSE > getCompilerOption("suppressUndefined") [1] ".Generic" ".Method" ".Random.seed" ".self"</syntaxhighlight>

Компиляция функций и выражений

Компиляция функций осуществляется с помощью функции cmpfun(). Возьмём пример из документации к функции cmpfun(), вкотором предлагается сравнить производительность различных реализаций функции lapply(), а также их скомпилированных версий:

КодR

<syntaxhighlight lang="r"># old R version of lapply la1 <- function(X, FUN, ...) { FUN <- match.fun(FUN) if (!is.list(X)) X <- as.list(X) rval <- vector("list", length(X)) for(i in seq(along = X)) rval[i] <- list(FUN(X[[i]], ...)) names(rval) <- names(X) # keep `names' ! return(rval) } # a small variation la2 <- function(X, FUN, ...) { FUN <- match.fun(FUN) if (!is.list(X)) X <- as.list(X) rval <- vector("list", length(X)) for(i in seq(along = X)) { v <- FUN(X[[i]], ...) if (is.null(v)) rval[i] <- list(v) else rval[[i]] <- v } names(rval) <- names(X) # keep `names' ! return(rval) }</syntaxhighlight>

Скомпилируем эти функции в бат-код:

КодR

<syntaxhighlight lang="r">> la1c <- cmpfun(la1) > la2c <- cmpfun(la2) > lapplyc <- cmpfun(lapply)</syntaxhighlight>

Сравним производительность этих функций:

КодR

<syntaxhighlight lang="r">> microbenchmark(lapply(x, is.null), la1(x, is.null), la2(x, is.null), + lapplyc(x, is.null), la1c(x, is.null), la2c(x, is.null)) Unit: microseconds expr min lq median uq max neval lapply(x, is.null) 174.1 181.2 196.4 229.5 946 100 la1(x, is.null) 786.0 816.2 838.6 985.6 1812 100 la2(x, is.null) 967.6 996.9 1085.8 1359.9 2417 100 lapplyc(x, is.null) 173.4 181.1 196.8 214.7 333 100 la1c(x, is.null) 392.0 409.8 433.8 502.3 1400 100 la2c(x, is.null) 403.6 415.6 439.7 532.1 1361 100</syntaxhighlight>

Обращается на себя внимание, что скомпилированная версия функции lapply() не превосходит по производительности оригинальную версию. Происходит это потому, что функция lapply() использует написанную на C, скомпилированную функцию. Также отметим, что скомпилированные версии функций la1() и la2() показываются практически одинаковую производительность, тогда как нескомпилированные версии довольно сильно различались. Это достигается за счёт высокой оптимизации работы циклов при компиляции функций.

Компиляция скриптов

Ещё одна возможность, предоставляемая пакетом compiler - это компиляция R-скриптов. Создание скомпилированных файлов осуществляется с помощью функции \texttt{cmpfile()}, а загрузка скомпилированных файлов с помощью функции loadcmp(). Если выходной файл не указан, то выходной файл имеет тоже имя, что и входной, но с расширением .Rc. Пример использования:

КодR

<syntaxhighlight lang="r">> cmpfile("script.R", "script.Rc") > loadcmp("script.Rc")</syntaxhighlight>

Примечания