-2

如何对函数进行基准测试?查看 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被称为。我可以相信输出还是有一些消除事物的黑魔法。

4

1 回答 1

1

在对函数进行基准测试时,您需要考虑的事项很少。

1) 确保缓存未命中不会显着影响结果。在您的情况下,您会遍历大量数据,其中会出现大量缓存未命中。改用更小的数组,它可以轻松放入 L1 缓存并循环多次。

2)确保您正在分析的函数调用有副作用,编译器无法优化这些调用。在您的情况下,编译器在优化方面做得不好,因为pow()即使没有副作用,调用也没有优化。更喜欢使用整数副作用来避免浮点性能异常(例如,将原始浮点数转换为 uint32 并将它们相加,而不是使用浮点数进行加法)。

3)展开你的循环几次,以减少循环的开销。目前,每个循环只执行一次 pow,其中循环为这个简单的函数调用增加了相对较大的开销。

4) 启用全面优化和内联的配置文件。

5) 多次运行分析,以确保其他过程不会影响您的结果。选择最好的结果进行比较(即来自其他过程的干扰最少)。

于 2014-08-09T14:34:58.037 回答