相关疑难解决方法(0)

基准测试,代码重新排序,易失性

我决定要对特定函数进行基准测试,所以我天真地编写如下代码:

#include <ctime>
#include <iostream>

int SlowCalculation(int input) { ... }

int main() {
    std::cout << "Benchmark running..." << std::endl;
    std::clock_t start = std::clock();
    int answer = SlowCalculation(42);
    std::clock_t stop = std::clock();
    double delta = (stop - start) * 1.0 / CLOCKS_PER_SEC;
    std::cout << "Benchmark took " << delta << " seconds, and the answer was "
              << answer << '.' << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

一位同事指出,我应该声明startstop变量volatile以避免代码重新排序.他建议优化器可以,例如,有效地重新排序代码,如下所示:

    std::clock_t start = std::clock();
    std::clock_t stop = …
Run Code Online (Sandbox Code Playgroud)

c++ benchmarking volatile compiler-optimization

17
推荐指数
1
解决办法
4121
查看次数

MSVC中的"Escape"和"Clobber"等效

Chandler Carruth的CppCon 2015演讲中,他介绍了两种神奇的功能,可以在没有任何额外性能损失的情况下击败优化器.

作为参考,这里是函数(使用GNU样式的内联汇编):

void escape(void* p)
{
    asm volatile("" : : "g"(p) : "memory");
}

void clobber()
{
    asm volatile("" : : : "memory");
}
Run Code Online (Sandbox Code Playgroud)

它适用于任何支持GNU样式内联汇编的编译器(GCC,Clang,Intel编译器,可能还有其他编译器).但是,他提到它在MSVC中不起作用.

检查Google Benchmark的实现,似乎他们使用重新解释转换为a volatile const char&并将其传递给隐藏在非gcc/clang编译器上的不同翻译单元中的函数.

template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
    internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
}

// some other translation unit
void UseCharPointer(char const volatile*) {}
Run Code Online (Sandbox Code Playgroud)

但是,我对此有两个顾虑:

  1. 我可能会招致一个函数调用
  2. 有一种"聪明"的链接时优化器可能会识别出UseCharPointer很小,内联它,然后丢弃我想要保留的所有代码,或者可以允许"聪明"的优化器执行其他重新排序我不这样做想要它.

MSVC中是否有与GNU样式的汇编函数相同的低级别?或者这是MSVC上最好的?

benchmarking microbenchmark visual-c++

17
推荐指数
1
解决办法
780
查看次数

在基准测试时防止编译器优化

我最近遇到了这个辉煌的cpp2015演讲CppCon 2015:Chandler Carruth"调优C++:基准测试,CPU和编译器!哦,我的!"

提到的阻止编译器优化代码的技术之一是使用以下函数.

static void escape(void *p) {
  asm volatile("" : : "g"(p) : "memory");
}

static void clobber() {
  asm volatile("" : : : "memory");
}

void benchmark()
{
  vector<int> v;
  v.reserve(1);
  escape(v.data());
  v.push_back(10);
  clobber()
}
Run Code Online (Sandbox Code Playgroud)

我试图理解这一点.问题如下.

1)越过clobber逃脱的好处是什么?

2)从上面的例子看起来,clobber()可以防止先前的语句(push_back)被优化.如果是这样的话,为什么下面的代码片段不正确?

 void benchmark()
 {
     vector<int> v;
     v.reserve(1);
     v.push_back(10);
     clobber()
 }
Run Code Online (Sandbox Code Playgroud)

如果这不够混乱,那么愚蠢(FB的线程化lib)就会有一个更奇怪的实现

相关片段:

template <class T>
void doNotOptimizeAway(T&& datum) {
  asm volatile("" : "+r" (datum));
}
Run Code Online (Sandbox Code Playgroud)

我的理解是上面的片段通知编译器程序集块将写入数据.但是如果编译器发现没有这个数据的消费者,它仍然可以优化生成数据的实体吗?

我认为这不是常识,任何帮助表示赞赏!

c++ gcc clang performance-testing compiler-optimization

10
推荐指数
2
解决办法
2296
查看次数