Изменения

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

413 байтов добавлено, 14:45, 28 мая 2014
м
Загрузка 2881-compilation.wiki
<!-- R:Оптимизация/Компиляция в байт-код -->
 
 
 
{{CC-BY-4.0|author=автором Артём Клевцов}}
{{Pkg-req-notice}}
В данном материале рассматривается один из способов ускорения выполнения кода путём компиляции выражений, функций и скриптов R в [http://ru.wikipedia.org/wiki/Байт-код байт-код]. Байт-код - машинно-независимый код низкого уровня, генерируемый транслятором и исполняемый интерпретатором. Большинство инструкций байт-кода эквивалентны одной или нескольким командам ассемблера. Трансляция в байт-код занимает промежуточное положение между компиляцией в машинный код и интерпретацией.
Программа на байт-коде обычно выполняется интерпретатором байт-кода (обычно он называется виртуальной машиной, поскольку подобен компьютеру). Преимущество - в портируемости, т. е. один и тот же байт-код может исполняться на разных платформах и архитектурах. То же самое преимущество дают интерпретируемые языки. Однако, поскольку байт-код обычно менее абстрактный, более компактный и более «компьютерный», чем исходный код, эффективность байт-кода обычно выше, чем чистая интерпретация исходного кода, предназначенного для правки человеком.
Рассмотрим эти опции:
* <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] 2> getCompilerOption("suppressAll")#> [1] FALSE> getCompilerOption("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: microsecondsnanoseconds#> expr min lq median uq uq max neval#> eval(expr) 315.05 323.438 329.628 338.978 365.631 191031 200750 205432 210436 291039 100#> eval(exprc) 924 1.58 1202 1.883 1346 2.199 2.482 8.311 1628 11845 100</nowiki>}}
Компиляция функций осуществляется с помощью функции <code>cmpfun()</code>. Возьмём пример из документации к функции <code>cmpfun()</code>, вкотором предлагается сравнить производительность различных реализаций функции <code>lapply()</code>, а также их скомпилированных версий:
for(i in seq(along = X))
rval[i] <- list(FUN(X[[i]], ...))
names(rval) <- names(X) # keep `names' !
return(rval)
}
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:1000> 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) 174173.1 181 183.2 196193.4 229205.5 2 946 294.4 100#> la1(x, is.null) 786792.0 8163 836.2 1 838861.6 1 985929.9 1807.6 1812 100#> la2(x, is.null) 967965.6 9960 1012.9 1 1036.6 1085.8 1359.9 2417 2769.5 100#> lapplyc(x, is.null) 173171.5 182.4 181 189.1 196200.8 3 214265.7 333 6 100#> la1c(x, is.null) 392394.0 4092 416.8 0 433428.8 0 502446.3 1400 9 1217.8 100#> la2c(x, is.null) 403397.6 415 424.6 5 439.7 1 532457.1 1361 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-компиляция ==