时间与 "as-if" 规则
Timing vs the "as-if" rule
总的来说还是不错的question about the "as-if" rule,但是不知道在测量时间的时候有没有例外。
考虑这个(取自 here 稍作修改):
using std::chrono;
auto begin = steady_clock::now();
auto result = some_lengthy_calculation(some_params);
auto end = std::chrono::steady_clock::now();
std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;
允许编译器应用任何产生相同 result
的优化。这里的要点是“as-if”规则并不直接适用于测量时间。当然,在应用优化时,测量时间不应该是恒定的。
所以我的问题是:当根据“as-if”规则允许编译器将其重新排列为以下之一时,我如何能够使用上述代码可靠地测量时间?
auto temp = some_lengthy_calculation(some_params); // clever "optimization", precompute some stuff
auto begin = steady_clock::now();
auto result = temp; // yay, I can use it here to pretend to be faster
auto end = steady_clock::now();
std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;
甚至“更优化”:
std::cout << "Time diff = " << 42 <<std::endl;
std::cout << "Result = " << some_lengthy_calculation(some_params);
我想没有一个正常的编译器会这样做,但是到底是什么阻止了编译器进行这种“优化”?
TL;DR...
- 可以观察优化和未优化代码之间的运行时差异
- 如果允许编译器优化影响测量时间,是什么阻止编译器根本不为计时创建任何代码?
编译器不会那样做的,你大可放心。
虽然在纯理论上是允许的,但获取系统时间涉及系统调用,这对编译器来说是一个完整的黑盒子。
编译器无法围绕黑盒函数调用重新排序,因为它无法假定这不会产生任何副作用或包含任何障碍。
要应用 As-If 规则,编译器必须证明提议的更改对可观察的行为没有影响。您是正确的,时间的流逝不是可观察到的行为。但是,在重新排序函数的情况下,必须证明调用函数的顺序不会影响可观察到的行为。
使用计时功能将不可避免地涉及一些测量时间的机制,编译器将无法证明重新排序是安全的。例如,它可能涉及对它无法检查的不透明系统 API 函数或驱动程序函数的调用。如果我们举一个最明显的例子,一个单调的软件时钟,每次它的状态被获取时只前进 1 个时间单位,就没有办法证明调用顺序无关紧要,因为它确实很重要。
总的来说还是不错的question about the "as-if" rule,但是不知道在测量时间的时候有没有例外。
考虑这个(取自 here 稍作修改):
using std::chrono;
auto begin = steady_clock::now();
auto result = some_lengthy_calculation(some_params);
auto end = std::chrono::steady_clock::now();
std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;
允许编译器应用任何产生相同 result
的优化。这里的要点是“as-if”规则并不直接适用于测量时间。当然,在应用优化时,测量时间不应该是恒定的。
所以我的问题是:当根据“as-if”规则允许编译器将其重新排列为以下之一时,我如何能够使用上述代码可靠地测量时间?
auto temp = some_lengthy_calculation(some_params); // clever "optimization", precompute some stuff
auto begin = steady_clock::now();
auto result = temp; // yay, I can use it here to pretend to be faster
auto end = steady_clock::now();
std::cout << "Time diff = " << duration_cast<microseconds>(end - begin).count() <<std::endl;
std::cout << "Result = " << result;
甚至“更优化”:
std::cout << "Time diff = " << 42 <<std::endl;
std::cout << "Result = " << some_lengthy_calculation(some_params);
我想没有一个正常的编译器会这样做,但是到底是什么阻止了编译器进行这种“优化”?
TL;DR...
- 可以观察优化和未优化代码之间的运行时差异
- 如果允许编译器优化影响测量时间,是什么阻止编译器根本不为计时创建任何代码?
编译器不会那样做的,你大可放心。
虽然在纯理论上是允许的,但获取系统时间涉及系统调用,这对编译器来说是一个完整的黑盒子。
编译器无法围绕黑盒函数调用重新排序,因为它无法假定这不会产生任何副作用或包含任何障碍。
要应用 As-If 规则,编译器必须证明提议的更改对可观察的行为没有影响。您是正确的,时间的流逝不是可观察到的行为。但是,在重新排序函数的情况下,必须证明调用函数的顺序不会影响可观察到的行为。
使用计时功能将不可避免地涉及一些测量时间的机制,编译器将无法证明重新排序是安全的。例如,它可能涉及对它无法检查的不透明系统 API 函数或驱动程序函数的调用。如果我们举一个最明显的例子,一个单调的软件时钟,每次它的状态被获取时只前进 1 个时间单位,就没有办法证明调用顺序无关紧要,因为它确实很重要。