为什么mean()这么慢?

Ant*_*zée 27 optimization performance r

一切都在问题中!我只是试着做一些优化,并且为了好奇而把钉子钉在脖子上,我试过了:

t1 <- rnorm(10)
microbenchmark(
  mean(t1),
  sum(t1)/length(t1),
  times = 10000)
Run Code Online (Sandbox Code Playgroud)

结果是mean()比"手动"计算慢6倍!

它是否源于在调用Internal(mean)之前mean()代码的开销,还是C代码本身更慢?为什么?有充分的理由,因此是一个很好的用例吗?

mne*_*nel 34

这是由于s3查找方法,然后在mean.default中对参数进行必要的解析.(以及其他代码的意思)

sum并且length都是原始函数.所以会很快(但你如何处理NA值?)

t1 <- rnorm(10)
microbenchmark(
  mean(t1),
  sum(t1)/length(t1),
  mean.default(t1),
  .Internal(mean(t1)),
  times = 10000)

Unit: nanoseconds
                expr   min    lq median    uq     max neval
            mean(t1) 10266 10951  11293 11635 1470714 10000
  sum(t1)/length(t1)   684  1027   1369  1711  104367 10000
    mean.default(t1)  2053  2396   2738  2739 1167195 10000
 .Internal(mean(t1))   342   343    685   685   86574 10000
Run Code Online (Sandbox Code Playgroud)

内部位meansum/ 更快length.

有关更多详细信息,请参阅http://rwiki.sciviews.org/doku.php?id=packages:cran:data.table#method_dispatch_takes_time(mirror)(以及避免使用的data.table解决方案.Internal).

请注意,如果我们增加向量的长度,那么原始方法是最快的

t1 <- rnorm(1e7)
microbenchmark(
     mean(t1),
     sum(t1)/length(t1),
     mean.default(t1),
     .Internal(mean(t1)),
+     times = 100)

Unit: milliseconds
                expr      min       lq   median       uq      max neval
            mean(t1) 25.79873 26.39242 26.56608 26.85523 33.36137   100
  sum(t1)/length(t1) 15.02399 15.22948 15.31383 15.43239 19.20824   100
    mean.default(t1) 25.69402 26.21466 26.44683 26.84257 33.62896   100
 .Internal(mean(t1)) 25.70497 26.16247 26.39396 26.63982 35.21054   100
Run Code Online (Sandbox Code Playgroud)

现在方法调度只是所需总体"时间"的一小部分.

  • @JoshO'Brien`base :: mean`对向量进行两次传递(在C级别)以校正数值精度(参见src/summary.c).顺便说一句,data.table优化的平均值也可以保留这个不错的功能并且与基本平均值完全相同.`sum`是单个传递,`length`在向量的标题中(无循环). (4认同)

Jos*_*ich 24

mean 由于以下几个原因,比"手动"计算慢:

  1. S3方法调度
  2. NA 处理
  3. 错误修正

第1点和第2点已经涵盖.第3点讨论在R用什么算法计算均值?.基本上,mean在向量上进行2次传递以纠正浮点错误.sum只有1次通过矢量.

请注意,由于这些精确问题,identical(sum(t1)/length(t1), mean(t1))可能是FALSE

> set.seed(21); t1 <- rnorm(1e7,,21)
> identical(sum(t1)/length(t1), mean(t1))
[1] FALSE
> sum(t1)/length(t1) - mean(t1)
[1] 2.539201e-16
Run Code Online (Sandbox Code Playgroud)