考虑一下这个简单的基准
list1 <- as.list(rep(1, 50))
list2 <- as.list(rep(1, 50))
microbenchmark::microbenchmark(
+ map2(list1, list2, sum))
Unit: microseconds
expr min lq mean median uq max neval
map2(list1, list2, sum) 375.31 384.2045 481.8708 407.8115 420.641 7923.58 100
microbenchmark::microbenchmark(
+ mapply(sum, X=list1, Y=list2, SIMPLIFY = FALSE))
Unit: microseconds
expr min lq mean median uq max neval
mapply(sum, X = list1, Y = list2, SIMPLIFY = FALSE) 46.187 50.634 57.45634 53.3715 59.8715 127.27 100为什么purrr:map2 比mapply慢8倍?我的意思是,我只是在两个列表中并排把数字相加。
问题是我在当前代码中使用的是map2,所以我想了解这里的开销是什么(以及如何修复它)。
谢谢!
发布于 2018-05-31 11:11:07
正如@eipi10 10在注释中所指出的,当使用更多的数据时,一些函数调用开销就变得不那么重要了:
list1 <- as.list(rep(1, 50000))
list2 <- as.list(rep(1, 50000))
microbenchmark(map2(list1, list2, sum), mapply(sum, X=list1, Y=list2, SIMPLIFY = FALSE))
Unit: milliseconds
expr min lq mean median uq max neval cld
map2(list1, list2, sum) 73.84420 78.21917 82.53853 79.48526 81.28048 218.9266 100 b
mapply(sum, X = list1, Y = list2, SIMPLIFY = FALSE) 51.92849 54.66514 61.34755 56.99206 58.67459 204.2119 100 a mapply使用.Internal,而purr::map2使用.Call访问执行处理的底层C函数。它们的工作方式有一些不同,特别是围绕参数计算,以及R搜索底层代码的方式。
.Internal上的R帮助给出了一个神秘的信息:
.Internal执行对内置到R解释器的内部代码的调用。 只有真正的R向导甚至应该考虑使用这个函数,并且只有R开发人员可以添加到内部函数的列表中。
然而,“R内部手册”解释说:
在构建时编译到R中的C代码可以在所谓的原语中直接调用,也可以通过.Internal接口调用,除了语法之外,它与.External接口非常相似。更准确地说,R维护一个R函数名称表和相应的C函数调用,按照惯例,这些函数都以‘do_’开头,并返回一个SEXP。此表(文件src/main/names.c中的R_FunTab)还指定需要或允许函数的多少参数,函数在调用之前是否要计算参数,以及函数是否是“内部的”,即函数必须通过.Internal接口访问,或者在这种情况下以R作为.Primitive直接访问。
和
少数原语是特殊的,而不是内置的,也就是说,它们是用未评估的参数输入的。这对于语言结构和赋值操作符,以及有条件地计算其第二个参数的&&和_ ~、.Internal、调用、表达式、缺失、on.exit、引号和替换都是必要的,后者不计算它们的某些参数。
.Call的帮助文件说明:
如果要经常使用这些函数之一,请指定包(将搜索限制在单个DLL上)或将.NAME传递为本机符号对象之一。搜索符号可能需要很长时间,特别是当加载了许多名称空间时。
这意味着在使用.Call时需要花费一些时间搜索DLL中的函数。值得注意的是,purr::map2在使用.Call时没有指定包名,这样做可以减少所需的开销。
https://stackoverflow.com/questions/50608312
复制相似问题