为什么在函数中运行R代码更快?

ifsvaxew  于 2023-03-27  发布在  其他
关注(0)|答案(2)|浏览(159)

请考虑以下示例:

> start<-Sys.time()
> for(i in 1:10000){}
> Sys.time()-start
Time difference of 0.01399994 secs
> 
> fn<-function(){
+   start<-Sys.time()
+   for(i in 1:10000){}
+   Sys.time()-start
+ }
> fn()
Time difference of 0.00199604 secs


start<-Sys.time()
for(i in 1:10000){x<-100}
Sys.time()-start
Time difference of 0.012995 secs
fn<-function(){
  start<-Sys.time()
  for(i in 1:10000){x<-100}
  Sys.time()-start
}
fn()
Time difference of 0.008996964 secs

增加迭代次数后的结果相同,如下所示:

> sim<-10000000
> start<-Sys.time()
> for(i in 1:sim){x<-i}
> Sys.time()-start
Time difference of 2.832 secs
> 
> fn<-function(){
+   start<-Sys.time()
+   for(i in 1:sim){x<-i}
+   Sys.time()-start
+ }
> fn()
Time difference of 2.017997 secs

我猜这不是巧合!为什么R代码在函数中运行得更快?

jexiocij

jexiocij1#

R中的函数是由JIT编译器编译的。在这之后,大多数函数都会更快。
正如?compiler::enableJIT中的文档所说,
如果参数为0,则禁用JIT。如果level为1,则在第一次使用之前编译较大的闭包。如果level为2,然后一些小的闭包在第二次使用之前也会被编译。如果level是3,那么所有顶级循环在执行之前都会被编译。JIT level 3要求编译器选项optimize为2或3。JIT级别也可以通过将环境变量R_ENABLE_JIT设置为这些值之一来启动R来选择。使用负参数调用enableJIT将返回当前JIT级别。默认JIT级别为3。
所以很多函数会比顶级代码更快。

hgb9j2n6

hgb9j2n62#

为了证明JIT的影响,我使用了这个基准测试:

library(microbenchmark)

compiler::enableJIT(0)  # use 3 for testing with full JIT compiler

fn <- function() {
   for(i in 1:10000) {}
}

microbenchmark(for_loop_without_func = for(i in 1:10000) {},
               for_loop_in_func = fn(),
               times = 100)

# Run eg. with (to avoid RStudio or other overhead):
# R --vanilla < jit_test.R

结果显示,禁用JIT后,执行时间几乎相同:

Unit: microseconds
                  expr     min       lq     mean   median      uq     max neval
 for_loop_without_func 180.619 180.7990 182.7129 180.9290 181.050 239.489   100
      for_loop_in_func 182.582 182.7075 186.2232 182.7625 182.938 309.912   100

使用compiler::enableJIT(3)(默认值),函数速度更快:

Unit: microseconds
                  expr     min       lq      mean   median       uq      max neval
 for_loop_without_func 558.727 574.4875 659.21931 657.3425 702.6475 1984.351   100
      for_loop_in_func  53.019  53.4955  61.59588  53.7260  54.0320  790.632   100

有趣的是,启用JIT似乎会减慢在函数外部运行的代码(与第一个“无JIT”基准测试相比),即使它不会被优化。理解为什么会很有趣(也许JIT需要时间来找出它不会优化的代码)?

相关问题