为什么 sapply 中的 sum 突然比 base::colSums 快?

问题描述 投票:0回答:1

我刚刚意识到使用

sapply(df, sum)
base::colSums(df)
更快。使用矩阵时也是如此
base::colSums(M)
。为什么?我错过了什么吗?一直都是这样吗?

基准

10K×10K数据帧的基准测试结果:

$ Rscript --vanilla speed_test.R 
Unit: milliseconds
      expr       min        lq      mean   median       uq       max neval  cld
    sapply 125.76717 125.77749 126.21036 125.7878 126.4320 127.07610     3 a   
   colSums 288.09562 293.57566 298.28873 299.0557 303.3853 307.71486     3  b  
 colSums_M 137.68780 139.08794 141.25548 140.4881 143.0393 145.59055     3   c 
  colSums2  55.49845  55.86153  56.04262  56.2246  56.3147  56.40479     3    d

注意: AlmaLinux 9.5 上的 R 版本 4.4.2 (2024-10-31)。 NETLIB 或 OPENBLAS-OPENMP(无关紧要),AMD Ryzen 7 7700X。


代码:

set.seed(42)

m <- 1e4; n <- 1e4
M <- matrix(rnorm(m*n), m, n)
df <- data.frame(M)

options(width=200)

microbenchmark::microbenchmark(
  sapply=sapply(df, sum),
  colSums=colSums(df),
  colSums_M=colSums(M),
  colSums2=matrixStats::colSums2(M),
  times=3L,
  check='equivalent'
  ) |> print()
r performance sapply
1个回答
1
投票

原因是 data.frame 被强制转换为矩阵,这是一个昂贵的操作。我们可以分析

colSums
:

Rprof(tmp <- tempfile(), interval = 0.002)
y <- colSums(df)
Rprof(NULL)
summaryRprof(tmp)
unlink(tmp)

在我的系统上,“by.total”中排名靠前的条目是:

#                       total.time total.pct self.time self.pct
#"colSums"                   0.048        96     0.018       36
#"as.matrix.data.frame"      0.030        60     0.002        4
#"as.matrix"                 0.030        60     0.000        0
#"unlist"                    0.022        44     0.022       44

可以看到总时间的60%都花在了

as.matrix
。 R 循环比
as.matrix(x)
和 C 循环的组合更快。

此外,从算法的角度来看,循环列表的元素比循环向量并跟踪元素属于哪一列更快。

PS:您不应该在这里使用微基准测试,因为垃圾收集器可能会扭曲计时。最好使用 bench 包,它可以过滤掉

gc
处于活动状态的迭代。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.