R中的“自包含”功能是否更有效?

时间:2019-01-14 12:51:06

标签: r performance function

我正在编写一个函数,该函数需要迭代地调用作为参数传递给列表的每个元素的函数g

我想知道如何使其尽可能快。我可以使用Rcpp和特定种类的g(用Cpp编写所有内容)来达到可接受的速度,但是我不知道是否可以通过将R函数用作参数来达到类似的速度。

正在做一些测试以找出R变慢的原因并发现了一些意想不到的结果:

minus <- function(x) -x
minus_vec <- Vectorize(minus, "x")

使用一些简单的函数来反转符号进行测试。

f0 <- function(x) {
  sapply(x, minus)
}

f1 <- function(x) {
  for(i in seq_along(x)){
    x[i] <- -x[i]
  }
  x
}

f2 <- function(x) {
  for(i in seq_along(x)){
    x[i] <- minus(x[i])
  }
  x
}

我得到以下结果:

a <- 1:10^5
library(rbenchmark)
benchmark(f0(a), f1(a), f2(a), minus_vec(a), minus(a))[,c(1,4)]

          test relative
1        f0(a)  454.842
2        f1(a)   25.579
3        f2(a)  178.211
4 minus_vec(a)  523.789
5     minus(a)    1.000

我想对以下几点进行一些解释:

  • 为什么f1f2的速度不一样?当他们做完全相同的事情时,编写代码-x[i]和调用函数minus(x[i])真的应该是如此不同吗?

  • 为什么f0f2慢?我一直认为apply函数比for循环更有效,但从来没有真正理解为什么,现在我什至找到了一个反例。

  • 是否可以使用函数f1来使函数的运行速度与minus一样快?

  • 为什么对minus进行矢量化(由于-已经被矢量化,所以不必要,但是不一定总是如此)使它变得如此糟糕?

2 个答案:

答案 0 :(得分:2)

不是完整的答案,但是这里有一些注意事项

1 minus(x)-x无所事事总比做某事好

您的函数minus调用`-`,因此添加的步骤会增加计算时间。老实说,我不知道具体是谁,什么时候,什么时候,换句话说,我不知道应该有多少计算时间。

下面是一个突出显示它的示例:我们有四个函数,所有的平方都是数字

fa <- function (n) n^2
fb <- function (n) fa(n)
fc <- function (n) fb(n)
fd <- function (n) fc(n)
Fa <- function (n) {
  for (i in seq_along(n)) n[i] <- fa(i)
  n
}
Fb <- function (n) {
  for (i in seq_along(n)) n[i] <- fb(i)
  n
}
Fc <- function (n) {
  for (i in seq_along(n)) n[i] <- fc(i)
  n
}
Fd <- function (n) {
  for (i in seq_along(n)) n[i] <- fd(i)
  n
}

这是基准测试结果

n <- 1:10^4
b <- benchmark(Fa(n),Fb(n),Fc(n),Fd(n), replications = 1000L)
b
#    test replications elapsed relative user.self sys.self user.child sys.child
# 1 Fa(n)         1000    3.93    1.000      3.85     0.00         NA        NA
# 2 Fb(n)         1000    7.08    1.802      6.94     0.02         NA        NA
# 3 Fc(n)         1000   10.16    2.585      9.94     0.06         NA        NA
# 4 Fd(n)         1000   13.68    3.481     13.56     0.00         NA        NA
# looks rather even
diff(b$elapsed)
# [1] 3.15 3.08 3.52

现在返回您的minus功能

a <- 1:10^5
b <- benchmark(f0(a), f1(a), f2(a), minus_vec(a), minus(a))          
b$elapsed[b$test == 'f2(a)'] - b$elapsed[b$test == 'f1(a)']    
# [1] 3.39   

2 apply vs for vs Vectorize

@NavyCheng提供了有关该主题的一些很好的材料。现在我的理解是,apply系列(就像Vectorize一样)在R中循环(而如果我没有误会`-`的循环是在{{1 }})。

同样,我不知道确切的细节,但是如果C使用apply/Vectorize循环,那么从理论上讲(实际上是经常),编写适当的R循环,其效果会更好或更好。


3 功能与 for一样快:

是临时的,我的结局是使用f1软件包通过作弊进行的。 (作弊,因为首先要在Rcpp中编写函数)

C ++

c++

现在使用#include <RcppArmadillo.h> //[[Rcpp::depends(RcppArmadillo)]] using namespace Rcpp; // [[Rcpp::export]] NumericVector minusCpp(NumericVector x) { for (int k = 0; k < x.length(); ++k) { x[k] = -x[k]; } return x; } 中的标记标记

R

答案 1 :(得分:1)

忽略 -x [i] minus(-x [i]),我将四个问题总结为两个:

  • 为什么 apply 家族比 forloop 慢?
  • 为什么 Vectorize apply 系列要慢?

第一个问题:

  

apply 功能旨在方便阅读,   不一定很快。

apply 系列将比 forloop

  

同样,sapply函数首先使用as.vector(unlist(...))将任何内容转换为向量,最后尝试将答案简化为合适的形式。

您无法阅读herehere以获得更多详细信息。

对于第二个问题,这是因为 Vectorize mapply 的包装,如果您在Rstudio中输入Vectorize,则会看到详细代码。您可以阅读this以获得更多帮助。