我决定要对特定函数进行基准测试,所以我天真地编写如下代码:
#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)
一位同事指出,我应该声明start和stop变量volatile以避免代码重新排序.他建议优化器可以,例如,有效地重新排序代码,如下所示:
std::clock_t start = std::clock();
std::clock_t stop = …Run Code Online (Sandbox Code Playgroud) 在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)
但是,我对此有两个顾虑:
MSVC中是否有与GNU样式的汇编函数相同的低级别?或者这是MSVC上最好的?
我最近遇到了这个辉煌的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)
我的理解是上面的片段通知编译器程序集块将写入数据.但是如果编译器发现没有这个数据的消费者,它仍然可以优化生成数据的实体吗?
我认为这不是常识,任何帮助表示赞赏!