遵循本文档条目中的Rcpp示例,考虑以下难以矢量化的函数,该函数创建一个长度为len的向量,其中指定了第一个元素(first)并且每个元素x_i都等于cos(x_{i-1} + 1):
repeatedCosPlusOne <- function(first, len) { x <- numeric(len) x[1] <- first for (i in 2:len) { x[i] <- cos(x[i-1] + 1) } return(x)}
在不重写一行代码的情况下加快此类功能的一种简单方法是使用R编译包对字节进行代码编译:
library(compiler)repeatedCosPlusOneCompiled <- cmpfun(repeatedCosPlusOne)
生成的函数通常会明显更快,同时仍返回相同的结果:
all.equal(repeatedCosPlusOne(1, 1e6), repeatedCosPlusOneCompiled(1, 1e6)) # [1] TRUE system.time(repeatedCosPlusOne(1, 1e6)) # user system elapsed # 1.175 0.014 1.201 system.time(repeatedCosPlusOneCompiled(1, 1e6)) # user system elapsed # 0.339 0.002 0.341
在这种情况下,字节编译加快了难以向量化的操作,该向量对长度为100万的向量从1.20秒到0.34秒。
备注
repeatedCosPlusOne作为单个功能的累积应用,的本质可以用以下方式更透明地表达Reduce:
iterFunc <- function(init, n, func) { funcs <- replicate(n, func) Reduce(function(., f) f(.), funcs, init = init, accumulate = TRUE) } repeatedCosPlusOne_vec <- function(first, len) { iterFunc(first, len - 1, function(.) cos(. + 1)) }
repeatedCosPlusOne_vec可以视为的“向量化” repeatedCosPlusOne。但是,可以预期它会慢2倍:
library(microbenchmark)microbenchmark( repeatedCosPlusOne(1, 1e4), repeatedCosPlusOne_vec(1, 1e4) ) #> Unit: milliseconds #> expr min lq mean median uq max neval cld #> repeatedCosPlusOne(1, 10000) 8.349261 9.216724 10.22715 10.23095 11.10817 14.33763 100 a #> repeatedCosPlusOne_vec(1, 10000) 14.406291 16.236153 17.55571 17.22295 18.59085 24.37059 100 b