在释放模式下运行程序

running the program in the Release mode

本文关键字:运行 程序 模式 释放      更新时间:2023-10-16

我想测试大循环中某些代码的执行速度,例如1000000000次。当我在调试模式下运行程序时,我看到超过3.192秒的持续时间,但在发布模式下,持续时间是0.000。我用biger计数器测试了一下,但它是0.000秒。对于我在发布模式中用于计数器(如10000000000)的每一个较大的数字,这个过程和结果都是相等的
问题出在哪里?

我的代码是:

#include<time.h>
#include<iostream>
int main() {
    clock_t t1, t2;

double d1 = 97.9834;
double d2 = 897.9134;
double d3 = 0.0;
t1 = clock();
for (size_t i = 0; i < 1000000000; i++)
{
    d3 = d1 + d2;
}
t2 = clock();
char msg[100];
sprintf_s(msg, 100, "time is :%.3fn", double(t2 - t1) / 1000.0);
cout << msg << endl;

return 0;
}

编译器需要生成优化的代码,它的行为"就像"它没有做任何优化(除了一些例外,如快速数学浮点优化),除了执行时间。您可以关闭优化,但这样就不会测量优化的代码。

问题的根源是,你无法测量你试图测量的东西,或者你已经得到了正确的结果(循环被优化了,这是应该的)。然而,如果您想强制编译器生成代码,则有一个关键字:volatile

volatile double d1 = 97.9834;
volatile double d2 = 897.9134;
volatile double d3 = 0.0;

现在,编译器需要将变量放入内存,真正读取d1和d2的值,并将结果写入d3,当循环中的加法语句执行此操作时,次数与循环迭代次数一样多。您只能使其中的一个或两个易失性,所以编译器可以跳过一些代码或将非易失性变量保留在寄存器中,这样循环会更快。

在使其中一些变量不稳定后,你是否会测量一些有用的东西,这取决于你实际想要测量的。。。

更多信息:C和C++中volatile的目的是告诉编译器,这个变量有点像内存映射的硬件寄存器,读取或写入它有一些外部影响,所以它永远不能被优化掉。它在PC程序中很少有用,尽管常见的误解与多线程编程无关(不像Java)。

我相信这个for循环在生产代码中被优化了,因为它没有做任何有用的事情:你从来没有使用过d3的值,为什么还要计算它呢?

如果希望执行此循环,请关闭优化(将-O0传递给编译器)。

除了for循环被完全优化之外,clock()函数并不是世界上最好的测量时间的工具。对于初学者来说,您肯定想做的是开始对clock()刻度变化进行测量(与高精度计数器相比,clock()刻度粒度相对较低)。参考CLOCKS_PER_SEC,例如在MSVC中只有1000。当在clock()周期的中间开始测量时,结果将非常不一致。

常见的模式是这样做以提高结果的一致性:

clock_t start;
const clock_t tmp = clock();
do {
    start = clock();
} while (tmp == start);
// execute the code
const clock_t end = clock();

您必须利用性能测试的结果来防止不必要的优化。一种方法是使变量"不稳定":

#include<time.h>
#include<iostream>
volatile double d = 0;
int main() {
    clock_t t1 = clock();
    for (size_t i = 0; i < 1000000000; i++)
    {
        d = 0;
    }
    clock_t t2 = clock();
    std::cout << "time is " << t2 - t1 << std::endl;
    // Debug:   3474486
    // Release:  484208
    return 0;
}

您需要以某种方式向优化器隐藏操作的无用性。但是,由于您想测量优化操作所需的时间,因此在说服优化器不要完全删除操作的过程中,您不希望破坏操作本身的优化。

如果您使d3d1d2中的一个具有易失性,则无法对d3 = d1 + d2;执行太多操作,您可以阻止优化器消除您想要测量的任何工作,但代价是添加一些您可能不想要测量的工作。

在其他情况下,有更好的方法可以防止优化器删除您想要测量的内容。在较大的情况下,一种常见的方法是将关键部分放入不同的编译单元,这样优化器就看不到没有做什么。但在这种情况下,这样做的调用开销甚至比volatile的额外成本还要糟糕。

如果问题是一个简化的"为什么测量时间在发布中不起作用",并且你真的想测量比d3 = d1 + d2;更复杂的东西,那么你可能会得到更好的帮助,询问你真正想测量什么。但如果目标真的是d3 = d1 + d2;,那就没有答案了。所花费的时间完全是如何使用它的函数,包括当优化器发现它没有用时,它所花的时间为零。