为什么在比较范围内的数字时,汇编代码中是否出现分支?

Ase*_*sal 6 c c++ math optimization performance

我正在读这个问题,这是接受的答案.我阅读了评论,但我无法弄清楚产生优化的原因.

使用以下代码时,为什么在汇编代码中出现分支?

x >= start && x <= end
Run Code Online (Sandbox Code Playgroud)

编辑:
为清楚起见,我想了解接受答案产生的优化原因.据我所知,编译器生成的汇编代码中存在分支.我想了解为什么生成的代码中有一个分支.

jas*_*son 10

请注意,链接的问题具有根本不同的表达

x >= start && x++ <= end
Run Code Online (Sandbox Code Playgroud)

它根本不同,因为这里的第二个子表达具有副作用.我会解释一下.

注意,这&&是一个短路操作员.这意味着如果x >= start评估为false,则机器可以分支评估x <= end.

更准确地说,当编译器发出指令时x >= start && x <= end,它可以发出指令,x <= endx >= start计算结果为false 时进行分支.

但是,我强调在上述陈述中使用单词can.这样做的原因是因为x <= end没有副作用,因此编译器是否对其进行分支并不重要.

但是,如果第二个表达式确实有副作用,编译器必须对其进行分支.由于&&是短路操作员,a && b如果b有任何副作用,如果评估,则不得观察afalse; 这是C和大多数(如果不是所有C语言)短路的要求.

所以,特别是当你看的时候

define POINT_IN_RANGE_AND_INCREMENT(p, range) 
    (p <= range.end && p++ >= range.start)
Run Code Online (Sandbox Code Playgroud)

请注意,第二个表达式p++ >= range.start有副作用.即,(后)递增p通过1.但只有在p <= range.end评估时才能观察到这种副作用true.因此,编译器必须p++ >= range.startif的p <= range.end计算结果进行分支.

The reason this results in a branch is because for machine to evaluate that expression, it uses the fact that if p <= range.end evaluates to false, then it automatically knows the entire expression evaluates to false and therefore it should not evaluate p++ >= range.start because it has a side-effect. Thus, it must branch over evaluating the second part of the expression. So in the assembly:

Ltmp1301:
 ldr    r1, [sp, #172] @ 4-byte Reload
 ldr    r1, [r1]
 cmp    r0, r1
 bls    LBB44_32
 mov    r6, r0         @ if the result of the compare is false 
 b      LBB44_33       @ branch over evaluating the second expression
                       @ to avoid the side effects of p++
LBB44_32:
 ldr    r1, [sp, #188] @ 4-byte Reload
 adds   r6, r0, #1
Ltmp1302:
 ldr    r1, [r1]
 cmp    r0, r1
 bhs    LBB44_36
Run Code Online (Sandbox Code Playgroud)

Deep indebtedness to Oli Charlesworth for insightful comments.

  • @Jason:在OP的代码中,没有副作用,所以短路是无关紧要的.在链接问题的代码中,有(正如你正确指出的)副作用,**会产生影响. (2认同)
  • @Zel这两个问题是完全不同的野兽,关于什么样的优化是可能的.链接的问题在后面部分有副作用,这意味着编译器被迫进行短路以确保正确性.如果没有副作用,不进行短路会导致更好的性能. (2认同)
  • 当副作用存在或不存在时,"可能"和"必须"之间存在差异.如果没有副作用,编译器可能会根据需要重新排列(速度),但如果有副作用 - 并且有 - 编译器*必须不重新排列.将其包含在答案中可能会有所帮助. (2认同)