使用volatile来防止基准测试代码中的编译器优化?

时间:2011-05-25 19:55:17

标签: c++

我正在创建一个小程序来衡量boost::shared_ptrboost::intrusive_ptr类型的容器之间的性能差异。为了防止编译器优化掉副本,我将变量声明为volatile。循环看起来像这样:

// TestCopy measures the time required to create n copies of the given container.
// Returns time in milliseconds.
template<class Container>
time_t TestCopy(const Container & inContainer, std::size_t n) {
    Poco::Stopwatch stopwatch;
    stopwatch.start();
    for (std::size_t idx = 0; idx < n; ++idx)
    {
        volatile Container copy = inContainer; // Volatile!
    }

    // convert microseconds to milliseconds
    return static_cast<time_t>(0.5 + (double(stopwatch.elapsed()) / 1000.0));
}

其余代码可以在这里找到:main.cpp

  • 这里使用volatile会阻止编译器优化副本吗?
  • 是否存在可能导致结果无效的陷阱?

更新

回应@Neil Butterworth。即使在使用副本时,我仍然认为编译器可以轻松避免复制:

for (std::size_t idx = 0; idx < n; ++idx)
{
    // gcc won't remove this copy?
    Container copy = inContainer;
    gNumCopies += copy.size();        
}

3 个答案:

答案 0 :(得分:7)

C ++ 03标准表示对易失性数据的读写是可观察的行为(C ++ 2003,1.9 [intro.execution] / 6)。我相信这可以保证无法优化对易失性数据的分配。另一种可观察行为是对I / O函数的调用。 在这方面,C ++ 11标准更加明确:在1.9 / 8中它明确地说明了

  

符合实施的最低要求是:
   - 严格按照抽象机的规则评估对volatile对象的访问。

如果编译器可以证明代码不会产生可观察的行为,那么它可以优化代码。在您的更新中(不使用volatile),复制构造函数和其他函数调用&amp;重载运算符可能会避免任何I / O调用和对volatile数据的访问,编译器可能会很好地理解它。但是,如果gNumCopies是一个全局变量,后来在具有可观察行为的表达式中使用(例如打印),则不会删除此代码。

答案 1 :(得分:4)

挥发性不太可能达到您对非POD类型的期望。我建议将容器的char *void *别名传递给不同翻译单元中的空函数。由于编译器无法分析指针的使用情况,因此这将充当编译器内存屏障,至少迫使对象进入处理器缓存,并阻止大多数死值消除优化。

答案 2 :(得分:3)

为什么要这样?最好的解决方案是以某种方式使用容器,例如将其大小添加到全局变量。