c++ - 禁用优化后 quick-bench.com 上的基准测试速度要快得多

标签 c++ optimization microbenchmark google-benchmark

我创建了一个非常简单的基准测试来说明短字符串优化,并在 quick-bench.com 上运行它。该基准测试在比较禁用/启用 SSO 的字符串类方面非常有效,结果与 GCC 和 Clang 非常一致。然而,我意识到当我禁用优化时,报告的时间比启用优化(-O2-O3)观察到的时间快 4 倍左右,GCC 和 clang 。

基准在这里:http://quick-bench.com/DX2G2AdxUb7sGPE-zLRa41-MCk0 .

知道什么可能导致未优化的基准运行速度快 4 倍吗?

不幸的是,我看不到生成的程序集;不知道问题出在哪里(选中“记录反汇编”框但对我的运行没有影响)。此外,当我使用 Google Benchmark 在本地运行基准测试时,结果符合预期,即优化后的基准测试运行得更快。

我还尝试在 Compiler Explorer 中比较这两种变体,未优化的变体似乎执行了更多的指令:https://godbolt.org/z/I4a171 .

最佳答案

因此,正如评论中所讨论的那样,问题在于 quick-bench.com 并未显示基准测试代码的绝对时间,而是相对于无操作基准测试所用时间的时间。空操作基准可以在 quick-bench.com 的源文件中找到:

static void Noop(benchmark::State& state) {
    for (auto _ : state) benchmark::DoNotOptimize(0);
}

一次运行的所有基准测试都是一起编译的。因此,优化标志也适用于它。

Reproducing and comparing the no-op benchmark for different optimization levels可以看到,从 -O0-O1 版本大约有 6 到 7 倍的加速。当比较使用不同优化标志完成的基准测试运行时,必须考虑基线中的这个因素来比较结果。因此,在问题的基准测试中观察到的 4 倍加速不仅仅是补偿,而且行为确实符合人们的预期。

-O0-O1 之间的空操作编译的一个主要区别是对于 -O0 有一些断言和google-benchmark 代码中的其他附加分支,已针对更高的优化级别进行了优化。

此外,在 -O0 循环的每次迭代都会多次加载到寄存器、修改和存储到 state 的内存部分,例如用于递减循环计数器和循环计数器上的条件,而 -O1 版本会将 state 保存在寄存器中,从而无需在循环中加载/存储内存。前者要慢得多,每次迭代至少需要几个周期来进行必要的存储转发和/或从内存中重新加载。

关于c++ - 禁用优化后 quick-bench.com 上的基准测试速度要快得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58167392/

相关文章:

android - 如何在 C++ 中通过 OpenGLES 在 android 中显示位图?

c++ - 使用 'lower_bound'

delphi - Delphi 有快速的 GetToken 例程吗?

mysql - 从 3 个左连接选择查询非常慢

java - 在 Java : Is my benchmarking code wrong? 中对小型数组与列表进行基准测试

c++ - 为什么我在使用 &this 时会出错?

c++ - 将类分离到它们自己的定义和实现文件中,并将 main 放在它自己的文件中

Python - 通过不在模块级别导入来优化?

r - 如何在微基准测试中使用列表参数

java - 如何用 Java 编写正确的微基准测试?