使用microbenchmark测试R欧氏距离函数时的速度困惑
为什么封装函数后某些欧氏距离计算逻辑反而更快?
我实现了两种计算两点欧氏距离的R逻辑,用microbenchmark测试时发现一个奇怪的现象:第一种逻辑的直接表达式速度较慢,但封装成函数后反而更快;第二种逻辑封装前后速度变化不大。
测试代码
xy1 = c(-1,-13) xy2 = c(-17,-26) distXY1 = function() return(sqrt((xy2[1] - xy1[1]) ^ 2 + (xy2[2] - xy1[2]) ^ 2)) distXY2 = function() return(sqrt(sum((xy2-xy1)^2))) microbenchmark( sqrt((xy2[1]-xy1[1])^2 + (xy2[2]-xy1[2])^2), distXY1(), sqrt(sum((xy2-xy1)^2)), distXY2(), times=100000 )
测试结果
Unit: nanoseconds expr min lq mean median uq max neval sqrt((xy2[1] - xy1[1])^2 + (xy2[2] - xy1[2])^2) 287 369 447.2313 369 410 1176085 1e+05 distXY1() 82 123 173.7059 164 164 1881490 1e+05 sqrt(sum((xy2 - xy1)^2)) 123 205 254.1065 205 205 1133363 1e+05 distXY2() 123 205 257.6268 205 246 2316008 1e+05
原因分析
distXY1的速度差异
直接执行的表达式sqrt((xy2[1]-xy1[1])^2 + (xy2[2]-xy1[2])^2)每次运行都要重新完成语法解析、逐个索引求值的步骤,这些重复的解析开销在高频调用时会被放大。而函数distXY1()在定义时就已经被R编译成了字节码,调用时直接执行预编译后的代码,省去了重复解析表达式的过程,所以速度反而更快。distXY2的速度持平
表达式sqrt(sum((xy2-xy1)^2))本身是简洁的向量运算,大部分开销集中在向量计算上,表达式解析的额外成本很低。封装成函数后,函数调用的基础开销(比如环境查找、调用栈处理)抵消了预编译带来的优化收益,所以最终速度和原表达式几乎没有差异。
如果想验证编译的作用,可以手动用compiler::cmpfun()编译对应的匿名函数,会发现直接表达式的编译版本速度和函数调用接近:
compiled_expr1 = compiler::cmpfun(function() sqrt((xy2[1]-xy1[1])^2 + (xy2[2]-xy1[2])^2)) microbenchmark(compiled_expr1(), distXY1(), times=100000)
内容的提问来源于stack exchange,提问作者anonR
相关产品推荐
相关产品推荐

