do.call比R中的正常呼叫慢20%?

时间:2013-07-13 10:54:17

标签: r function-call

我不确定我是否以正确的方式使用do.call

test <- function(test) {
  string <- deparse(substitute(test))
  start <- regexpr("\\(", string)
  end <- regexpr(")", string) - 1
  distribution <- substr(string, 0, start-1)
  string.arguments <- substr(string, start+1, end)
  v <- read.table(text=unlist(strsplit(string.arguments, ",")))
  list.arguments <- lapply(t(v), function(x) x)

  for (i in 1:1000000) {
    do.call(distribution, list.arguments)
  } 
}

此处的目标是能够发送分发,例如rnormrgamma,然后发送函数的参数,而不是评估函数。

这是使用do.call和只是简单地调用函数的比较:

> system.time(test(rnorm(100, 1, 10))) 
   user  system elapsed 
   17.772   0.000  17.820 
> system.time(for(i in 1:1000000) { rnorm(100,0,1)} )
   user  system elapsed 
   13.940   0.004  14.015 

问题有两个:

  • do.call真的需要花费20%的时间吗?
  • 这是接受不同分配和参数的正确方法吗?

1 个答案:

答案 0 :(得分:10)

do.call总是比直接调用函数慢,因为它必须通过你的参数并在调用之前找到函数。它的缓慢程度取决于它有多少额外的计算来分摊这个开销。

> system.time(for(i in 1:1e6) do.call(rnorm, list(100)))
   user  system elapsed 
  13.55    0.00   13.58 
> system.time(for(i in 1:1e6) rnorm(100))
   user  system elapsed 
  11.40    0.00   11.42 

,而:

> system.time(for(i in 1:1e2) do.call(rnorm, list(1e6)))
   user  system elapsed 
   9.14    0.00    9.15 
> system.time(for(i in 1:1e2) rnorm(1e6))
   user  system elapsed 
   9.14    0.00    9.14 

此外,你的一些减速是由于你的正则表达式和其他字符串操作,这与do.call本身的速度无关。虽然快速,因为它运行在一个小的输入,它仍然是不必要的复杂。为什么不这样做:

test <- function(distrib, ..., N=1e6)
lapply(seq(N), function(x) distrib(...))

test(rnorm, 100, 1, 10)

或者这个:

test <- function(call, N=1e6)
{
    call <- substitute(call)
    lapply(seq(N), function(...) eval.parent(call))
}

test(rnorm(100, 1, 10))