使用内联函数定义将列表元素与 lapply 相乘的速度几乎是使用标准“*”的两倍

Multiplying elements of list with lapply is almost twice as fast with in-line function definition than with standard "*"

如果我们想用一个常数乘以一个列表的元素,我们可以用lapply来实现。但是,我观察到定义要在线应用的函数比指定“*”作为要应用的函数快几乎两倍:

library(microbenchmark)
microbenchmark::microbenchmark(x=lapply(X=list(a=c(1,2,3)), FUN=function(x) x*1000), y=lapply(X=list(a=c(1,2,3)), "*", 1000), times = 10000)

第一个表达式的中值约为 1100 纳秒,第二个表达式的中值约为 1900 纳秒。

知道为什么会发生这种情况吗?

lapply 调用 match.fun,它必须花费一些时间(嗯,大约一微秒)将字符串 "*" 与原始函数 `*` 匹配。直接传递函数可以避免开销。

l <- list(1, 2, 3)
microbenchmark::microbenchmark(lapply(l, function(x) x * 1000),
                               lapply(l, "*", 1000),
                               lapply(l, `*`, 1000),
                               times = 1e+06L)
## Unit: nanoseconds
##                             expr  min   lq     mean median   uq      max neval
##  lapply(l, function(x) x * 1000) 1271 1435 1614.497   1476 1517  1243981 1e+06
##             lapply(l, "*", 1000) 1640 1763 2026.791   1804 1886 16498605 1e+06
##             lapply(l, `*`, 1000)  861  984 1198.956   1025 1066 16636365 1e+06
microbenchmark::microbenchmark(match.fun(function(x) x * 1000),
                               match.fun("*"),
                               match.fun(`*`),
                               times = 1e+06L)
## Unit: nanoseconds
##                             expr min  lq      mean median  uq      max neval
##  match.fun(function(x) x * 1000)  82 164  249.0617    205 205 15783606 1e+06
##                   match.fun("*") 779 902 1036.1593    902 984 15515261 1e+06
##                   match.fun(`*`)  41 164  187.4243    164 164   588842 1e+06

也就是说,match.fun 永远不会成为瓶颈,除非您可能编写了一个调用 match.fun 数十亿次的函数,因此在此级别进行优化只是“为了好玩"。