对单个函数进行基准测试
Posted
技术标签:
【中文标题】对单个函数进行基准测试【英文标题】:Benchmark a single function 【发布时间】:2014-08-09 13:29:00 【问题描述】:如何对函数进行基准测试?查看 callgrind 的结果,我发现我的程序在pow
中花费了很多时间。由于我不需要完整的工作精度,我认为我可以创建一个查找表并在表中的点之间使用线性插值。为了能够评估查找表方法,我需要测量时间。所以我这样做了:
#ifdef __WAND__
target[name[test2.exe] type[application] platform[;Windows]]
target[name[test2] type[application]]
#endif
#include <herbs/main/main.h>
#include <herbs/tictoc/tictoc.h>
#include <herbs/array_fixedsize/array_fixedsize.h>
#include <random>
#include <cstdio>
#include <cmath>
class GetRand
public:
GetRand(double min,double max):U(min,max)
bool operator()(double* val,size_t n,size_t N)
*val=U(randsource);
return 1;
private:
std::mt19937 randsource;
std::uniform_real_distribution<double> U;
;
int MAIN(int argc,charsys_t* argv[])
Herbs::ArrayFixedsize<double> vals(1024*1024*128,GetRand(-4,4));
const size_t N=16;
auto n=N;
while(n)
double start=0;
auto ptr=vals.begin();
Herbs::TicToc timestamp(start);
while(ptr!=vals.end())
pow(2,*ptr);
++ptr;
// I have set cpu-freq to 1.6 GHz using cpufreq-set
printf("%.15g\t",1.6e9*start/vals.length());
--n;
return 0;
运行此程序时,每次迭代的输出约为 2.25 个周期。这似乎很低,因为pow
的实现似乎是(它callgrind
给了我__ieee754_pow
)。
在 x86-64 上为 GNU/Linux 编译时,汇编中的基准循环如下所示:
call _ZN5Herbs6TicTocC1ERd@PLT
movq %r14, %rbx
.p2align 4,,10
.p2align 3
.L28:
vmovsd (%rbx), %xmm1
vucomisd .LC6(%rip), %xmm1
jb .L25
vmovsd .LC7(%rip), %xmm0
call pow@PLT
.L25:
addq $8, %rbx
cmpq %r12, %rbx
jne .L28
movq %rbp, %rdi
call _ZN5Herbs6TicTocD1Ev@PLT
至少调用了pow
。我可以相信输出还是有一些消除事情的黑魔法。
【问题讨论】:
带代码的问题需要有语言标签。不然怎么会有人知道这是什么? @Thomas,基准测试在所有语言中都有相同的问题:非确定性环境、编译器和链接器优化。第一个可以通过多次运行测试来消除,第二个要复杂得多。 请注意,如果您只需要 2 的幂,则有一个函数exp2
。
@MarcGlisse 我也试过这个,但如果基准算法有效,它在 x86-MinGW 上会慢 10 倍。但在回答哪个功能最有效之前,我需要确保基准测试有效。
试试-fno-builtin-pow
。如果您使用调试器或跟踪器或任何东西,您会注意到 pow 实际上并未在您的程序中调用。 jb
让您跳过通话。或者至少将 pow
的结果存储在 volatile 变量中。
【参考方案1】:
在对函数进行基准测试时,您需要考虑的事项很少。
1) 确保缓存未命中不会显着影响结果。在您的情况下,您会遍历大量数据,其中会出现大量缓存未命中。改用更小的数组,它可以轻松放入 L1 缓存并循环多次。
2) 确保您正在分析的函数调用有副作用,编译器无法优化这些调用。在您的情况下,编译器在优化方面做得不好,因为即使没有副作用,pow()
调用也没有优化出来。更喜欢使用整数副作用来避免浮点性能异常(例如,将原始浮点数转换为 uint32 并将它们相加,而不是使用浮点数进行加法)。
3) 展开循环几次以减少循环的开销。目前,每个循环只执行一次 pow,其中循环为这个简单的函数调用增加了相对较大的开销。
4) 启用全面优化和内联的配置文件。
5) 多次运行分析,以确保其他进程不会影响您的结果。选择最佳结果进行比较(即来自其他进程的干扰最少)。
【讨论】:
以上是关于对单个函数进行基准测试的主要内容,如果未能解决你的问题,请参考以下文章