使用-O3或-Ofast来编译基准代码还是删除代码是否现实?

时间:2015-07-02 22:41:22

标签: c++ optimization g++ compiler-optimization compiler-flags

当使用-O3编译下面的基准代码时,我对它在延迟方面的差异印象深刻,所以我开始怀疑编译器是否不是"作弊"通过某种方式删除代码。有没有办法检查?我可以安全地使用-O3进行基准测试吗?期望速度提高15倍是否现实?

没有-O3的结果:平均值: 239 nanos最小值:230纳米(900万次迭代)
-O3的结果:平均值: 14 nanos,最小值:12纳米(900万次迭代)

int iterations = stoi(argv[1]);
int load = stoi(argv[2]);

long long x = 0;

for(int i = 0; i < iterations; i++) {

    long start = get_nano_ts(); // START clock

    for(int j = 0; j < load; j++) {
        if (i % 4 == 0) {
            x += (i % 4) * (i % 8);
        } else {
            x -= (i % 16) * (i % 32);
        }
    }

    long end = get_nano_ts(); // STOP clock

    // (omitted for clarity)
}

cout << "My result: " << x << endl;

注意:我使用clock_gettime来衡量:

long get_nano_ts() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000000000 + ts.tv_nsec;
}

3 个答案:

答案 0 :(得分:2)

在启用优化编译时,编译器肯定会“作弊”并删除不必要的代码。实际上,加速代码的速度非常快,几乎总能带来令人印象深刻的加速。如果它能以某种方式导出一个公式来计算恒定时间的结果,而不是使用这个循环,它会。常数因子15并非一帆风顺。

意味着您应该分析未优化的版本!实际上,当使用像C和C ++这样的语言时,未优化构建的性能几乎完全没有意义。你根本不必担心。

当然,这可能会干扰上面显示的微基准测试。两点:

  1. 这种微观优化通常也无关紧要。更喜欢分析您的实际程序,然后消除瓶颈。
  2. 如果您确实需要这样的微基准测试,请依赖于某些运行时输入并显示结果。这样,编译器就无法自行删除功能,只需使其速度相当快。
  3. 由于您似乎正在这样做,因此您展示的代码很有可能成为合理的微观基准。您应该注意的一件事是您的编译器是否将对get_nano_ts();的两次调用都移动到循环的同一侧。允许这样做,因为“运行时间”不算作可观察到的副作用。 (该标准甚至不要求你的机器以有限的速度运行。)有人认为here这通常不是问题,但我无法判断给出的答案是否有效。

    如果您的程序没有做任何其他昂贵的事情,那么您想要进行基准测试(如果可能的话,不应该做任何事情),您也可以将时间测量“移到”外部,例如:与time

答案 1 :(得分:1)

对您认为自己测量的内容进行基准测量可能非常困难。在内循环的情况下:

for (int j = 0;  j < load;  ++j)
        if (i % 4 == 0)
                x += (i % 4) * (i % 8);
        else    x -= (i % 16) * (i % 32);

精明的编译器可能能够看透并将代码更改为:

 x = load * 174;   // example only

我知道这不是等价的,但有一些相当简单的表达式可以取代那个循环。

可以肯定的是使用gcc -S编译器选项并查看它生成的汇编代码。

答案 2 :(得分:0)

您应该始终基准,并启用优化功能。但是,确保您想要的时间不会被编译器优化掉是非常重要的。

通过在计时器停止后打印出计算结果来实现此目的的一种方法:

long long x = 0;

for(int i = 0; i < iterations; i++) {

    long start = get_nano_ts(); // START clock

    for(int j = 0; j < load; j++) {
        if (i % 4 == 0) {
            x += (i % 4) * (i % 8);
        } else {
            x -= (i % 16) * (i % 32);
        }
    }

    long end = get_nano_ts(); // STOP clock

    // now print out x so the compiler doesn't just ignore it:
    std::cout << "check: " << x << '\n',

    // (omitted for clarity)
}

当比较几种不同算法的基准时,这些算法也可用作检查每种算法产生相同的结果。