Изменения

Перейти к: навигация, поиск

R:Оптимизация/Компиляция в байт-код

8063 байта добавлено, 14:45, 28 мая 2014
м
Загрузка 2881-compilation.wiki
<!-- R:Оптимизация/Компиляция в байт-код -->
 
 
 
{{CC-BY-4.0|author=автором Артём Клевцов}}
{{Pkg-req-notice}}
В данном материале рассматривается один из способов ускорения выполнения кода путём компиляции выражений, функций и скриптов R в [http://ru.wikipedia.org/wiki/Байт-код байт-код]. Байт-код - машинно-независимый код низкого уровня, генерируемый транслятором и исполняемый интерпретатором. Большинство инструкций байт-кода эквивалентны одной или нескольким командам ассемблера. Трансляция в байт-код занимает промежуточное положение между компиляцией в машинный код и интерпретацией.
Программа на байт-коде обычно выполняется интерпретатором байт-кода (обычно он называется виртуальной машиной, поскольку подобен компьютеру). Преимущество - в портируемости, т. е. один и тот же байт-код может исполняться на разных платформах и архитектурах. То же самое преимущество дают интерпретируемые языки. Однако, поскольку байт-код обычно менее абстрактный, более компактный и более «компьютерный», чем исходный код, эффективность байт-кода обычно выше, чем чистая интерпретация исходного кода, предназначенного для правки человеком.
В составе базовых пакетов R поставляется пакет {{r-package|compiler|core=true}}, который предоставляет входит состав ядра R, но не загружается по умолчанию при старте R-сессии. Данный пакет включает в себя ряд функций для компиляции R-кода в байт-код. == Параметры компиляции == Все рассмотренные ранее функции из пакета {{r-package|compiler|core=true}} имеют опции, которые могут быть переданы в качестве аргументов функциям компиляции (аргумент <code>options</code>), или заданы глобально с помощью функции <code>setCompilerOptions()</code>. Рассмотрим эти опции: * <code>optimize</code> - определяет уровень оптимизации: принимает значения от 0 до 3 (по умолчанию 2);* <code>suppressAll</code> - управляет сообщениями: принимает значения <code>TRUE</code> или <code>FALSE</code> (по умолчанию code>FALSE</code>);* <code>suppressUndefined</code> - управление сообщения о неопределённых (undefined) переменных: может принимать значения <code>TRUE</code> или список имён переменных (по умолчанию ".Generic", .Method", ".Random.seed", ".self"). Получить текущее значение глобальных опций компиляции можно с помощью функции <code>getCompilerOption()</code>: {{r-code|code=<nowiki>getCompilerOption("optimize")#> [1] 2getCompilerOption("suppressAll")#> [1] FALSEgetCompilerOption("suppressUndefined")#> [1] ".Generic" ".Method" ".Random.seed" ".self"</nowiki>}} == Компиляция функций и выражений == Скомпилировать отдельно взятое выражение можно с помощью функции <code>compile()</code>. {{r-code|code=<nowiki># объявляем переменныеs <- as.double(0)x <- as.double(1:1000)# объявляем выраженияexpr <- expression(for (i in x) s <- s + i)exprc <- compile(for (i in x) s <- s + i)# сравниваем результат работы выраженийidentical(eval(expr), eval(exprc))#> [1] TRUE# сравниваем производительность выраженийmicrobenchmark(eval(expr), eval(exprc))#> Unit: nanoseconds#> expr min lq median uq max neval#> eval(expr) 191031 200750 205432 210436 291039 100#> eval(exprc) 924 1202 1346 1628 11845 100</nowiki>}} Компиляция функций осуществляется с помощью функции <code>cmpfun()</code>. Возьмём пример из документации к функции <code>cmpfun()</code>, вкотором предлагается сравнить производительность различных реализаций функции <code>lapply()</code>, а также их скомпилированных версий: {{r-code|code=<nowiki># old R version of lapplyla1 <- 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 variationla2 <- 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)}</nowiki>}} Скомпилируем эти функции в бат-код: {{r-code|code=<nowiki>la1c <- cmpfun(la1)la2c <- cmpfun(la2)lapplyc <- cmpfun(lapply)</nowiki>}} Сравним производительность этих функций: {{r-code|code=<nowiki>x <- 1:1000microbenchmark(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) 173.1 183.2 193.4 205.2 294.4 100#> la1(x, is.null) 792.3 836.1 861.1 929.9 1807.6 100#> la2(x, is.null) 965.0 1012.1 1036.6 1085.9 2769.5 100#> lapplyc(x, is.null) 171.5 182.4 189.1 200.3 265.6 100#> la1c(x, is.null) 394.2 416.0 428.0 446.9 1217.8 100#> la2c(x, is.null) 397.6 424.5 439.1 457.2 1216.0 100</nowiki>}} Обращается на себя внимание, что скомпилированная версия функции <code>lapply()</code> не превосходит по производительности оригинальную версию. Происходит это потому, что функция <code>lapply()</code> использует написанную на C, скомпилированную функцию. Также отметим, что скомпилированные версии функций <code>la1()</code> и <code>la2()</code> показываются практически одинаковую производительность, тогда как нескомпилированные версии довольно сильно различались. Это достигается за счёт высокой оптимизации работы циклов при компиляции функций. == Компиляция скриптов == Ещё одна возможность, предоставляемая пакетом {{r-package|compiler|core=true}} - это компиляция R-скриптов. Создание скомпилированных файлов осуществляется с помощью функции \texttt{cmpfile()}, а загрузка скомпилированных файлов с помощью функции <code>loadcmp()</code>. Если выходной файл не указан, то выходной файл имеет тоже имя, что и входной, но с расширением <code>.Rc</code>. Пример использования: {{r-code|code=<nowiki>cmpfile("script.R", "script.Rc")loadcmp("script.Rc")</nowiki>}} == JIT-компиляция == Помимо компиляции отдельных функций и выражений, пакет {{r-package|compiler|core=true}} предоставляет возможность [http://ru.wikipedia.org/wiki/JIT-компиляция JIT-компиляции] (JIT - just-in-time) или компиляции «на лету», т.е. во время непосредственного выполнения кода. Переключение режима осуществляется с помощью функции <code>enableJIT()</code>. Эта функции имеет всего один аргумент (<code>level</code>), который может принимать одно из трёх значений: * 0 --- отключение JIT-компиляции;* 1 --- компиляции функций до их первого вызова;* 2 --- тоже что и 2 плюс компиляция всех циклов <code>for</code>, <code>while</code>, <code>repeat</code> до их вызова. Отметим, что при включении JIT-компиляции при первом запуске выполнения кода компиляция всех функций и циклов займёт некоторое время.
== Примечания ==
<references />
 
== Ссылки ==
 
* [http://homepage.stat.uiowa.edu/~luke/R/compiler/compiler.pdf A Byte Code Compiler for R]
[[Категория:R]]
[[Категория:Оптимизация кода]]

Навигация