Подтвердить что ты не робот

Возможные недостатки использования JIT с R?

Недавно я обнаружил, что с помощью пакета компилятора можно использовать компиляцию JIT (только вовремя) с помощью R (я обобщаю свои выводы по этой теме в недавнем сообщении в блоге).

Один из вопросов, которые меня задали, это:

Есть ли какая-нибудь ошибка? Звучит слишком хорошо, чтобы быть правдой, просто поставьте одну строку кода и что он.

После осмотра я мог найти одну возможную проблему, связанную с временем запуска JIT. Но есть ли еще одна проблема, чтобы быть осторожным при использовании JIT?

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

4b9b3361

Ответ 1

Приведенный выше пример rpart больше не кажется проблемой:

library("rpart")
fo = function() {
  for(i in 1:500){
    rpart(Kyphosis ~ Age + Number + Start, data=kyphosis)
  }
}    system.time(fo())
#   user  system elapsed 
#  1.212   0.000   1.206 
compiler::enableJIT(3)
# [1] 3
system.time(fo())
#   user  system elapsed 
#  1.212   0.000   1.210 

Я также пробовал ряд других примеров, таких как

  • рост вектора;
  • Функция, которая только обертка вокруг mean

Хотя я не всегда получаю ускорение, я никогда не испытываю значительного замедления.


R> sessionInfo()
R version 3.3.0 (2016-05-03)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04 LTS

Ответ 2

вывод простого теста с rpart может быть советом не использовать enableJIT во всех случаях:

library(rpart)
fo <- function() for(i in 1:500){rpart(Kyphosis ~ Age + Number + Start, data=kyphosis)}
system.time(fo())
#User      System verstrichen 
#2.11        0.00        2.11 

require(compiler)
enableJIT(3)
system.time(fo())
#User      System verstrichen 
#35.46        0.00       35.60

Любое объяснение?

Ответ 3

В принципе, как только байт-код скомпилирован и загружен, он всегда должен интерпретироваться как минимум так же быстро, как исходный интерпретатор АСТ. Некоторый код будет выигрывать от больших ускорений, обычно это код с большим количеством скалярных операций и циклов, где большинство времени тратится на интерпретацию R (я видел примеры с 10-кратным ускорением, но произвольные микро-тесты могут действительно раздувать это по мере необходимости). Некоторый код будет работать с одинаковой скоростью, обычно это код, хорошо векторизованный и, следовательно, почти не требует интерпретации. Теперь сама компиляция может быть медленной. Следовательно, компилятор только вовремя не компилирует функции, когда он догадывается, что он не окупится (и эвристика меняется со временем, это уже в 3.4.x). Эвристика не всегда угадывает это правильно, поэтому могут возникнуть ситуации, когда компиляция не окупится. Типичными проблемными шаблонами являются генерация кода, модификация кода и манипуляция привязками окружений, захваченных в закрытии.

Пакеты могут быть скомпилированы по байтам во время установки, так что стоимость компиляции не будет выплачиваться (повторно) во время выполнения, по крайней мере, для кода, который известен заранее. Теперь это значение по умолчанию в версии для версии R. Хотя загрузка скомпилированного кода происходит намного быстрее, чем компиляция, в некоторых ситуациях можно загружать даже код, который не будет выполнен, поэтому на самом деле могут быть накладные расходы, но в целом предварительная компиляция полезна. Недавно некоторые параметры GC были настроены для снижения стоимости кода загрузки, который не будет выполнен.

Моя рекомендация для авторов пакетов будет заключаться в использовании настроек по умолчанию (компиляция по принципу "точно в момент времени" включена по умолчанию в выпущенных версиях, байт-компиляция во время установки пакета включена в версию разработки). Если вы найдете пример, когда компилятор байт-кода не работает хорошо, отправьте отчет об ошибке (в предыдущих версиях я также видел случай с rpart). Я бы рекомендовал против генерации кода и обработки кода, и особенно в горячих циклах. Это включает определение замыканий, удаление и вставку привязок в средах, захваченных закрытием. Определенно, не следует делать eval(parse(text= в горячих циклах (и это уже было плохо без байтовой компиляции). Всегда лучше использовать ветки, чем динамически создавать новые затворы (без ветвей). Также лучше писать код с циклами, чем динамически генерировать код с огромными выражениями (без циклов). Теперь с помощью компилятора байт-кода теперь часто можно писать циклы, работающие на скалярах в R (производительность не будет такой же плохой, как раньше, поэтому можно было бы чаще избегать без переключения на C для критически важных частей).

Ответ 4

В дополнение к предыдущему ответу, эксперимент показывает, что проблема заключается не в компиляции цикла, а в компиляции замыканий. [enableJIT (0) или enableJIT (1) быстро покидают код, enableJIT (2) резко замедляет его, а enableJIT (3) немного быстрее, чем предыдущий параметр (но все еще очень медленный)]. Также в отличие от комментария Hansi, cmpfun замедляет выполнение в той же степени.