无法解释C++性能
Can't Explain C++ Performance
如果您有一个永不改变且始终等于零的变量 A、函数 F、函数 G 和函数 H,并在现代版本的 GCC 上使用 -O3 优化的现代英特尔台式机处理器上调用以下代码:
for(i = 0; i < a_big_number; i++)
{
if(A != 0) F();
else G();
}
执行需要 2 秒。请注意,F 从不调用,因为 A 始终为 0。或者
for(i = 0; i < a_big_number; i++)
{
if(A != 0) H();
else G();
}
只需 1 秒即可执行。同样,A 始终为 0,并且从不调用 H。最后
for(i = 0; i < a_big_number; i++)
{
G();
}
只需 0.5 秒即可执行。
给定前两个示例中的条件语句,为什么 F 和 H 的内容很重要?既然他们从未被召唤过,为什么他们的所作所为会有所不同?鉴于英特尔处理器具有复杂的分支预测,处理器难道不应该弄清楚 G() 总是被调用,甚至从不在条件语句上浪费时间吗?我知道有条件的指令应该浪费一些时间,但我不明白为什么它会浪费这么多时间。
假设编译器理解A
是一个常量,它应该将以下代码转换为:
for(i = 0; i < a_big_number; i++)
{
if(A != 0) F();
else G();
}
进入这个:
if(A != 0)
for(i = 0; i < a_big_number; ++i)
F();
else
for(i = 0; i < a_big_number; ++i)
G();
或者,如果常量似乎是编译时常量,则完全优化F()
函数调用。
如果没有发生这种情况(即可能存在副作用或其他原因 - 编译器不保证它如何优化以及优化什么),循环将遇到来自分支错误预测的性能影响。如果 A 没有变化并且被调用的函数足够小,CPU 应该锁定循环并记住分支,这样它就不会一遍又一遍地重复相同的错误。另一方面,循环可能会被展开,这可能会造成很大的伤害,因为它无法并行化,只会破坏代码大小和 CPU 必须跟踪的许多事情。
不过,你如何测量执行时间对我来说是一个谜,以及你在循环中调用的函数在做什么。例如,您可以测量流程执行换出。因此,除非您提供一个完整的工作示例并详细说明您的测量方法,否则不可能告诉您发生了什么。
无论如何,我敢打赌你的时间测量是不正确的,或者你在代码中做了一些你没有显示的坏事,或者以上所有。
据我所知,编译器无法确定分支是否不会执行。编译器能做的最好的事情就是预测哪个分支更有可能。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么constexpr的性能比正常表达式差
- 在类中使用随机生成器时出现性能问题
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 海湾合作委员会 ARM 性能下降
- GCC 和 Clang 代码性能的巨大差异
- 在容量内调整矢量大小时的性能影响
- 了解算法的性能差异(如果以不同的编程语言实现)
- 未达到的情况会影响开关外壳性能
- QStringList vs list<shared_ptr<QString>> 性能比较C++
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- 哪种方法更好,性能明智
- C++ 特征库:引用的性能开销<>
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- std::p mr::memory_resource 如何与 std::container 产生性能差异?
- 不同的类或结构初始化方法之间的性能差异是什么?