在下面的 C++ 示例中,我定义了一个计算树最左边路径高度的函数。
struct TreeNode {
int value{};
TreeNode *l = nullptr;
TreeNode *r = nullptr;
};
int getLeftHeight(TreeNode *root) {
int height = 0;
while (root != nullptr) {
root = root->l;
height++;
}
return height;
}
Run Code Online (Sandbox Code Playgroud)
使用 GCC 9.3 或 10.1(使用 -O3)编译时,我得到以下 x86。
getLeftHeight(TreeNode*):
xor eax, eax
test rdi, rdi
je .L4
.L3:
mov rdi, QWORD PTR [rdi+8]
add eax, 1
test rdi, rdi
jne .L3
ret
.L4:
ret
Run Code Online (Sandbox Code Playgroud)
但是,在使用 Clang 10 编译时,如下图所示,没有重复的ret.
getLeftHeight(TreeNode*):
xor eax, eax
test rdi, rdi
je .LBB0_3
.LBB0_1:
mov rdi, qword ptr [rdi + 8]
add eax, 1
test rdi, rdi
jne .LBB0_1
.LBB0_3:
ret
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会为什么要这样做?
据我了解,这ret将需要一个额外的字节进行编码。也许在这种情况下这并不重要,因为发出的机器代码的对齐/填充,但如果不是这种情况,并且这种重复发生在大型代码库中,许多字节的机器代码最终可能只是重复ret的 .
这种特殊的重复情况似乎也很容易发现和优化。
这似乎是 GCC 中遗漏的优化。
有一个错误#71923,其中有一个非常相似的示例,标记为“missed-optimization”。
话虽如此,对性能的影响是微不足道的——主要是代码大小。此外,当第二个ret对齐时(现代 x86 目标通常是这种情况),消除第一个ret通常不会对代码大小产生影响,但如果消除,从循环中失败将导致解码nop之前的附加指令ret,这会使该代码路径变慢。
无论如何,始终欢迎向 GCC 提出拉取请求。您可以查看优化过程列表,-fdump-passes并使用-fdump-tree-...标志转储 RTL/GIMPLE 树(或在Godbolt上查看)。主要问题是现有的优化通道是否应该解决这个问题,或者是否需要添加一个新的优化通道(我不是 GCC 方面的专家,所以不能以一种或另一种方式提出建议)。
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |