繁体   English   中英

为什么 x86-64 C/C++ 编译器没有为此代码生成更高效的程序集?

[英]Why are x86-64 C/C++ compilers not generating more efficient assembly for this code?

考虑以下局部变量的声明:

bool a{false};
bool b{false};
bool c{false};
bool d{false};
bool e{false};
bool f{false};
bool g{false};
bool h{false};

在 x86-64 架构中,我希望优化器将这些变量的初始化减少到类似于mov qword ptr [rsp], 0的内容。 但是,我能够尝试的所有编译器(无论优化级别如何)得到的是某种形式的:

mov     byte ptr [rsp + 7], 0
mov     byte ptr [rsp + 6], 0
mov     byte ptr [rsp + 5], 0
mov     byte ptr [rsp + 4], 0
mov     byte ptr [rsp + 3], 0
mov     byte ptr [rsp + 2], 0
mov     byte ptr [rsp + 1], 0
mov     byte ptr [rsp], 0

这似乎是在浪费 CPU 周期。 使用复制初始化、值初始化或用括号替换大括号没有区别。

但是等等,这还不是全部。 假设我有这个:

struct
{
    bool a{false};
    bool b{false};
    bool c{false};
    bool d{false};
    bool e{false};
    bool f{false};
    bool g{false};
    bool h{false};
} bools;

然后bools的初始化会产生我所期望的: mov qword ptr [rsp], 0 是什么赋予了?

您可以在此 Compiler Explorer 链接中自己尝试上面的代码。

不同编译器的行为是如此一致,以至于我不得不认为上面的低效率是有原因的,但我一直没能找到它。 你知道为什么吗?

编译器是愚蠢的,这是一个错过的优化。 mov qword ptr [rsp], 0将是最佳的。 从 qword 存储到任何单个字节的字节重新加载的存储转发在现代 CPU 上是有效的。 https://blog.stuffedcow.net/2014/01/x86-memory-disambiguation/

(或者更好的是, push 0而不是sub rsp, 8 + mov这也是一个错过的优化,因为编译器不会费心寻找可能的情况。)


据推测,寻找存储合并的优化过程会在确定堆栈帧中的局部变量相对于彼此的位置之前运行。 (或者甚至在决定哪些本地人可以保存在寄存器中以及哪些需要 memory 地址之前。)

存储合并又名合并最近才在 GCC8 IIRC 中重新引入,之后作为从 GCC2.95 到 GCC3 的回归,再次被 IIRC 删除。 (我认为其他优化,例如假设没有严格混叠违规以在更多时间将更多变量保留在寄存器中,更有用)。 所以它已经消失了几十年。

从一个 POV 来看,您可以说自己很幸运,您可以合并任何存储(结构成员和数组元素,它们很早就知道是相邻的)。 当然,从另一个 POV 来看,理想情况下,编译器应该制作好的 asm。 但在实践中,错过优化很常见。 幸运的是,我们有强大的 CPU,具有广泛的超标量乱序执行,通常可以通过这些废话来快速查看即将到来的缓存未命中加载和存储,因此浪费的指令有时有时间在其他瓶颈的阴影下执行。 这并不总是正确的,并且在乱序执行 window 中堵塞空间绝不是一件好事

相关: 在 x86-64 asm 中:如果源操作数是两个立即值,是否有办法优化两个相邻的 32 位存储/写入 memory? 涵盖除0以外的常量的一般情况,重新:最佳 asm 是什么。 (数组与单独的本地人之间的区别仅在评论中讨论过。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM