基准测试一个纯C++函数
Benchmarking a pure C++ function
如何防止GCC/Clang内联和优化纯函数的多次调用?
我正在尝试对这种形式的代码进行基准测试
int __attribute__ ((noinline)) my_loop(int const* array, int len) {
// Use array to compute result.
}
我的基准代码看起来像这样:
int main() {
const int number = 2048;
// My own aligned_malloc implementation.
int* input = (int*)aligned_malloc(sizeof(int) * number, 32);
// Fill the array with some random numbers.
make_random(input, number);
const int num_runs = 10000000;
for (int i = 0; i < num_runs; i++) {
const int result = my_loop(input, number); // Call pure function.
}
// Since the program exits I don't free input.
}
正如预期的那样,Clang似乎能够在O2(甚至可能在O1(将其转变为无操作。
我试着对我的实现进行基准测试的几件事是:
将中间结果累加为整数,并在最后打印结果:
const int num_runs = 10000000; uint64_t total = 0; for (int i = 0; i < num_runs; i++) { total += my_loop(input, number); // Call pure function. } printf("Total is %llun", total);
遗憾的是,这似乎不起作用。Clang至少足够聪明,意识到这是一个纯粹的函数,并将基准转换为这样的东西:
int result = my_loop(); uint64_t total = num_runs * result; printf("Total is %llun", total);
在每次循环迭代结束时使用发布语义设置一个原子变量:
const int num_runs = 10000000; std::atomic<uint64_t> result_atomic(0); for (int i = 0; i < num_runs; i++) { int result = my_loop(input, number); // Call pure function. // Tried std::memory_order_release too. result_atomic.store(result, std::memory_order_seq_cst); } printf("Result is %llun", result_atomic.load());
我的希望是,由于原子论引入了
happens-before
关系,Clang将被迫执行我的代码。但遗憾的是,它仍然进行了上述优化,并一次性将原子的值设置为num_runs * result
,而不是运行函数的num_runs
迭代。在每个循环的末尾设置一个volatile int,同时求和总数。
const int num_runs = 10000000; uint64_t total = 0; volatile int trigger = 0; for (int i = 0; i < num_runs; i++) { total += my_loop(input, number); // Call pure function. trigger = 1; } // If I take this printf out, Clang optimizes the code away again. printf("Total is %llun", total);
这似乎起到了作用,我的基准测试似乎有效。这并不理想,原因有很多。
根据我对C++11内存模型
volatile set operations
的理解,它没有建立happens before
关系,所以我不能确定某些编译器不会决定进行相同的num_runs * result_of_1_run
优化。此外,这种方法似乎是不可取的,因为现在我在每次运行循环时都要设置一个volatile int,这是一个开销(无论多么小(。
有没有一种规范的方法可以阻止Clang/GCC优化这个结果。也许是用实用主义者什么的?如果这种理想的方法在编译器中有效,则可获得额外的积分。
您可以直接在程序集中插入指令。我有时会使用宏来拆分程序集,例如将负载与计算和分支分离。
#define GCC_SPLIT_BLOCK(str) __asm__( "//nt// " str "nt//n" );
然后在源中插入
GCC_SPLIT_BLOCK("请保管"(
在您的函数之前和之后
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗