何时/为什么(a <0)可能在表达式中分支?

Cor*_*lks 9 c++

在阅读了关于这个问题的许多评论之后,有几个人(这里这里)建议这段代码:

int val = 5;
int r = (0 < val) - (val < 0); // this line here
Run Code Online (Sandbox Code Playgroud)

会引起分支.不幸的是,他们都没有给出任何理由或者说它为什么会导致分支(三角形暗示它需要类似cmove指令或预测,但并不真正说明原因).

这些人是否正确"表达中使用的比较不会产生分支"实际上是神话而不是事实?(假设你没有使用一些深奥的处理器)如果是这样,你能举个例子吗?

我以为不会有任何分支(假设没有逻辑"短路"),现在我很好奇.

chi*_*rlu 5

为简化问题,请仅考虑表达式的一部分:val < 0.从本质上讲,这意味着"如果val是否定的,则返回1,否则0"; 你也可以这样写:

val < 0 ? 1 : 0
Run Code Online (Sandbox Code Playgroud)

如何将其转换为处理器指令在很大程度上取决于编译器和目标处理器.最简单的方法是编写一个简单的测试函数,如下所示:

int compute(int val) {
    return val < 0 ? 1 : 0;
}
Run Code Online (Sandbox Code Playgroud)

并查看编译器生成的汇编代码(例如,with gcc -S -o - example.c).对于我的机器,它没有分支.但是,如果我将其更改为返回5而不是1,则有分支指令:

...
    cmpl    $0, -4(%rbp)
    jns     .L2
    movl    $5, %eax
    jmp     .L3
.L2:
    movl    $0, %eax
.L3:
...
Run Code Online (Sandbox Code Playgroud)

因此,"在表达式中使用的比较不会产生分支"确实是一个神话.(但"在表达式中使用的比较将始终生成分支"也不是真的.)


针对此扩展/澄清的补充:

我在问是否有任何(理智的)平台/编译器可能存在分支.MIPS/ARM/86(_64)/等.我正在寻找的是一个证明这是现实可能性的案例.

这取决于你认为是"理智"的平台.如果古老的6502 CPU系列是理智的,我认为val > 0没有分支就没有办法计算它.另一方面,大多数现代指令集提供某种类型的set-on-X指令.

(val < 0实际上即使在6502上也可以不进行分支计算,因为它可以实现为位移.)

  • 我个人认为`(val <0)*5;`比val <0更值得比较?5:0;`(是的,`(val <0)`和`val <0?1:0`是重言式,但我希望三元运算符更有可能分支比条件...是否或不是期望是否有效,我不知道).对我来说,g ++为`(val <0)*5`和`val <0生成完全相同的程序集?5:0` clang不会分支,并使用`cmove`表示`val <0?5:0`和一个`movzbl`,带有`'valull`,用于`(val <0)*5`.有趣! (2认同)