【问题标题】:why for loop has 1 extra instruction than expected?为什么 for 循环比预期多出 1 条指令?
【发布时间】:2023-03-28 06:28:01
【问题描述】:

我写了很多向量化的循环,所以1个常见的成语是

volatile int dummy[1<<10];
for (int64_t i = 0; i + 16 <= argc; i+= 16)   // process all elements with whole vector
{
  int x = dummy[i];
}
// handle remainder (hopefully with SIMD too)

但是生成的机器代码比我想要的多 1 条指令(使用 gcc 4.9)

.L3:
        leaq    -16(%rax), %rdx
        addq    $16, %rax
        cmpq    %rcx, %rax
        movl    -120(%rsp,%rdx,4), %edx
        jbe     .L3

如果我将代码更改为for (int64_t i = 0; i &lt;= argc - 16; i+= 16),那么“额外” 指令消失了:

.L2:
        movl    -120(%rsp,%rax,4), %ecx
        addq    $16, %rax
        cmpq    %rdx, %rax
        jbe     .L2

但为什么会有差异?我在想可能是由于循环不变量,但太模糊了。然后我注意到在 5 指令情况下,增量是在加载之前完成的,由于 x86 的破坏性 2 操作数指令,这将需要额外的 mov。 所以另一种解释可能是它的交易指令并行性为 1 条额外指令。

虽然看起来几乎不会有任何性能差异,但有人能解释一下这个谜团吗(最好谁知道编译器转换)?

理想情况下,我希望保留 i + 16

【问题讨论】:

  • 只问关于优化代码的问题,非优化代码对程序来说是字面意思,根本不代表速度。当您打开优化器时,您现在所拥有的将一无所有,显然您无法让它更快。
  • 但这是优化代码 (-O3),除了易失性负载吗?是为了用简单的方式来说明问题
  • 我同意 supercat 的回答非常精确,但它假设将 i + 16

标签: gcc optimization assembly x86 gcc4.9


【解决方案1】:

如果argc 低于-2147483632,并且i 低于2147483632,则表达式i+16 &lt;= argc 将需要产生算术正确的结果,而表达式和i&lt;argc-16 则不会。在这种极端情况下需要给出算术正确的结果会阻止编译器优化前一个表达式以匹配后者。

【讨论】:

  • 多么可怕的陷阱(谁会使用负数组大小)!我已经尝试将 argc 和 i 的类型都更改为 uint32_t 并且生成的指令计数仍为 5,因为仍可能发生下溢。但是当我将类型更改为 int64 和 uint64 时,指令数会减少到 4。这将是出乎意料的,因为仍然可能发生溢出,假设编译器已经如此严格?
  • 但是即使编译器没有将 i + 16 不同
  • @YaleZhang:如果类型是 uint32,那么 i+16 = 4294967280 或 argc
猜你喜欢
  • 2021-10-01
  • 1970-01-01
  • 2019-04-11
  • 2019-05-13
  • 2017-01-29
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 2022-06-12
相关资源
最近更新 更多