C++优化器重新排序对clock()的调用是否合法?

Pau*_*zak 69 c++ optimization clock

C++编程语言第4版,第225页读取:只要结果与简单执行顺序的结果相同,编译器就可以重新排序代码以提高性能.一些编译器,例如发布模式下的Visual C++,将重新排序此代码:

#include <time.h>
...
auto t0 = clock();
auto r  = veryLongComputation();
auto t1 = clock();

std::cout << r << "  time: " << t1-t0 << endl;
Run Code Online (Sandbox Code Playgroud)

进入这种形式:

auto t0 = clock();
auto t1 = clock();
auto r  = veryLongComputation();

std::cout << r << "  time: " << t1-t0 << endl;
Run Code Online (Sandbox Code Playgroud)

这保证了与原始代码不同的结果(零与大于零的时间报告).有关详细示例,请参阅我的其他问题 这种行为是否符合C++标准?

Jen*_*das 18

好吧,有一些叫做Subclause 5.1.2.3 of the C Standard [ISO/IEC 9899:2011]哪些州:

在抽象机器中,所有表达式都按语义指定进行计算.实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).

因此,我真的怀疑这种行为 - 你所描述的行为 - 符合标准.

此外 - 重组确实会对计算结果产生影响,但是如果从编译器的角度来看它 - 它存在于int main()世界中并且在进行时间测量时 - 它会窥视,要求内核给它当前时间,然后去回到主要世界,外面世界的实际时间并不重要.clock()本身不会影响程序,变量和程序行为不会影响clock()函数.

时钟值用于计算它们之间的差异 - 这就是您要求的.如果在两次测量之间发生了某些事情,则与编译器的角度无关,因为您要求的是时钟差异,测量之间的代码不会影响测量过程.

然而,这并未改变所描述的行为非常不愉快的事实.

尽管不准确的测量结果令人不愉快,但它可能会变得更糟,甚至更危险.

请考虑以下从此站点获取的代码:

void GetData(char *MFAddr) {
    char pwd[64];
    if (GetPasswordFromUser(pwd, sizeof(pwd))) {
        if (ConnectToMainframe(MFAddr, pwd)) {
              // Interaction with mainframe
        }
    }
    memset(pwd, 0, sizeof(pwd));
}
Run Code Online (Sandbox Code Playgroud)

正常编译时,一切正常,但如果应用优化,memset调用将被优化,这可能导致严重的安全漏洞.为什么要优化出来?这很简单; 编译器再次在其main()世界中思考并认为memset是一个死存储,因为pwd之后不使用该变量而不会影响程序本身.

  • 点.`std :: fill`会起作用.易失性指针仍然是迭代器.C++的可预测性比C更低. (5认同)
  • @sharpneli"除非编译器可以证明[...]",但这正是编译器在这里所​​做的.如果你隐藏了VeryLongComputation的主体,我强烈怀疑它重新排序.但是在这里它详细介绍了非常长的计算,证明它没有副作用,然后重新排序.不,不同的运行时间(挥动手臂说它是非常不同的)并不算作副作用. (2认同)

MSa*_*ers 13

编译器无法交换这两个clock调用.t1必须在之后设定t0.两种调用都是可观察到的副作用.只要观察结果与抽象机器的可能观察结果一致,编译器就可以对这些可观察效果之间的任何内容进行重新排序,甚至可以对可观察到的副作用进行重新排序.

由于C++抽象机器并未正式限制为有限速度,因此它可以veryLongComputation()在零时间内执行.执行时间本身未定义为可观察的效果.真实的实现可能与之匹配.

请注意,很多答案取决于C++标准,而不是对编译器施加限制.

  • 应该更明确:两个_calls_都是可观察到的副作用. (2认同)

o11*_*11c 7

是的,这是合法的 - 如果编译器可以看到clock()调用之间发生的全部代码.

  • @remyabel原始问题中的引用允许这样做.关键是要认识到`clock`的调用不是正在重新排序的 - 它就是计算.通过as-if规则,可以随时计算任何透明计算. (2认同)
  • @PaulJurczak这是正确的; 可观察行为是写入volatile变量,并调用库函数 (2认同)