将RIT与R一起使用可能存在的缺点?

时间:2012-04-11 13:20:03

标签: performance r compiler-construction jit

我最近发现可以使用编译器包使用JIT(及时)编译R(我在a recent blog post中总结了我对此主题的发现)。

我被问到的一个问题是:

  

是否有任何陷阱?这听起来好得令人难以置信,只需放一行   代码就是这样。

环顾四周后,我发现一个可能的问题与JIT的“启动”时间有关。但是在使用JIT时还有其他问题需要注意吗?

我想与R的环境架构有一些限制,但我想不出一个简单的问题,我的头脑,任何建议或危险信号都会有很大的帮助吗? / p>

4 个答案:

答案 0 :(得分:11)

使用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

任何解释?

答案 1 :(得分:4)

上面给出的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 :(得分:1)

原则上,一旦编译和加载了字节码,就应该始终将其解释为至少与原始AST解释器一样快。一些代码将受益于大加速,这通常是具有大量标量操作和循环的代码,其中大部分时间用于R解释(我已经看到了10倍加速的例子,但任意微基准测试确实可以根据需要对其进行充气)。一些代码将以相同的速度运行,这通常是代码很好地矢量化,因此几乎没有时间进行解释。现在,编译本身可能很慢。因此,即时编译器现在不会编译函数,因为它猜测它不会得到回报(并且启发式随时间变化,这已经在3.4.x中)。启发式方法并不总是猜对了,因此可能存在编译无法获得回报的情况。典型的有问题的模式是代码生成,代码修改和对闭包中捕获的环境的绑定的操作。

软件包可以在安装时进行字节编译,这样就不会在运行时(重复)支付编译成本,至少对于提前知道的代码而言。现在这是R的开发版本中的默认值。虽然加载编译代码比编译它快得多,但在某些情况下,可能会加载甚至不会被执行的代码,因此实际上可能存在开销,但整体预编译是有益的。最近,GC的一些参数已经过调整,以降低加载代码的成本。

我对软件包编写者的建议是使用默认值(默认情况下,在发布版本中,即时编译现已开启,软件包安装时的字节编译现在在开发版本中开启)。如果您找到字节码编译器效果不佳的示例,请提交错误报告(我在早期版本中也看到了涉及rpart的案例)。我建议不要使用代码生成和代码操作,特别是在热循环中。这包括在闭包捕获的环境中定义闭包,删除和插入绑定。绝对不应该在热循环中执行eval(parse(text=(并且这已经很糟糕,没有字节编译)。使用分支总是比动态生成新的闭包(没有分支)更好。使用循环编写代码比使用大表达式(没有循环)动态生成代码更好。现在使用字节码编译器,现在通常可以编写在R中的标量上运行的循环(性能不会像以前一样糟糕,所以人们可以更频繁地离开,而不会切换到C以获得性能关键份)。

答案 3 :(得分:-2)

继上一个答案之后,实验表明问题是与循环的编译,它与闭包的编译。 [enableJIT(0)或enableJIT(1)保持代码快速,enableJIT(2)显着减慢代码,enableJIT(3)比前一个选项略快(但仍然非常慢)]。与Hansi的评论相反,cmpfun在相似程度上减缓了执行速度。