在 C++ 中,分支预测器是否预测隐式条件语句?
In C++, does the branch predictor predict implicit conditional statements?
在这段代码中,写着,result += runs[i] > runs[i-1];
,一个隐含的条件语句。在 C++ 中,分支预测器是否对这条语句进行预测?还是我必须明确使用 if
关键字才能进行分支预测?
using namespace std;
int progressDays(vector<int> runs) {
if (runs.size() < 2) {return 0;}
int result = 0;
for (int i = 1; i < runs.size(); i++) {result += runs[i] > runs[i-1];}
return result;
}
CPU 不直接 运行 C++,它们 运行 机器代码。所以答案取决于您的 C++ 如何编译为汇编代码/机器代码。 您对用 C++ 表达程序逻辑的选择只会间接影响这一点。现代编译器 可以 并将 C++ if()
语句转换为没有分支的 asm(也称为无分支)。 (对于 GCC,-O3
比 -O2
做得更积极——见 )
在大多数架构中,有一些有效的方法可以相当直接地将比较结果转换为 0 或 1 整数。 (或者以不同的方式无分支递增,甚至更直接,例如 AArch64's csinc
/ csel
/ cinc
指令执行条件递增,读取输入寄存器和标志)。 所以一般使用x < y
作为整数值会无分支编译。
int conditional_inc(int x, int y, int z) {
z += (x<y);
return z;
}
例如,on the Godbolt compiler explorer
# x86-64 clang -O3
conditional_inc(int, int, int)
xor eax, eax # prepare a zeroed register for setl of the low byte, to extend to 32-bit. (x86 is annoyingly clunky and inefficient at this)
cmp edi, esi
setl al # EAX = AL = (x<y) signed compare
add eax, edx # EAX += z in the retval register
ret
AArch64 更高效,结合增量和 select 指令替换 xor-zero/setcc/add.
conditional_inc(int, int, int):
cmp w0, w1 // compare
cinc w0, w2, lt // use the flags result, and the other 2 inputs.
ret
所有这些,如 x86-64 setcc
,只是 ALU 指令,而不是控制(程序计数器没有条件变化),因此具有数据依赖性而不是控制依赖性,因此不要不需要分支预测,因为没有分支。 (最著名的此类指令可能是 x86 cmovcc
,但在这种情况下只需要 setcc
)
- Why is a conditional move not vulnerable for Branch Prediction Failure? 显示了使用 cmov 有条件递增的 asm 示例。
一般来说,比较与分支是分开的。您可以比较然后在没有分支的情况下获得布尔值。 (分支确实需要一些东西来分支,但这可以是与整数或布尔值的零进行隐式比较。)
所以这不是条件语句,它只是一个用作整数的布尔值。
在这段代码中,写着,result += runs[i] > runs[i-1];
,一个隐含的条件语句。在 C++ 中,分支预测器是否对这条语句进行预测?还是我必须明确使用 if
关键字才能进行分支预测?
using namespace std;
int progressDays(vector<int> runs) {
if (runs.size() < 2) {return 0;}
int result = 0;
for (int i = 1; i < runs.size(); i++) {result += runs[i] > runs[i-1];}
return result;
}
CPU 不直接 运行 C++,它们 运行 机器代码。所以答案取决于您的 C++ 如何编译为汇编代码/机器代码。 您对用 C++ 表达程序逻辑的选择只会间接影响这一点。现代编译器 可以 并将 C++ if()
语句转换为没有分支的 asm(也称为无分支)。 (对于 GCC,-O3
比 -O2
做得更积极——见
在大多数架构中,有一些有效的方法可以相当直接地将比较结果转换为 0 或 1 整数。 (或者以不同的方式无分支递增,甚至更直接,例如 AArch64's csinc
/ csel
/ cinc
指令执行条件递增,读取输入寄存器和标志)。 所以一般使用x < y
作为整数值会无分支编译。
int conditional_inc(int x, int y, int z) {
z += (x<y);
return z;
}
例如,on the Godbolt compiler explorer
# x86-64 clang -O3
conditional_inc(int, int, int)
xor eax, eax # prepare a zeroed register for setl of the low byte, to extend to 32-bit. (x86 is annoyingly clunky and inefficient at this)
cmp edi, esi
setl al # EAX = AL = (x<y) signed compare
add eax, edx # EAX += z in the retval register
ret
AArch64 更高效,结合增量和 select 指令替换 xor-zero/setcc/add.
conditional_inc(int, int, int):
cmp w0, w1 // compare
cinc w0, w2, lt // use the flags result, and the other 2 inputs.
ret
所有这些,如 x86-64 setcc
,只是 ALU 指令,而不是控制(程序计数器没有条件变化),因此具有数据依赖性而不是控制依赖性,因此不要不需要分支预测,因为没有分支。 (最著名的此类指令可能是 x86 cmovcc
,但在这种情况下只需要 setcc
)
- Why is a conditional move not vulnerable for Branch Prediction Failure? 显示了使用 cmov 有条件递增的 asm 示例。
一般来说,比较与分支是分开的。您可以比较然后在没有分支的情况下获得布尔值。 (分支确实需要一些东西来分支,但这可以是与整数或布尔值的零进行隐式比较。)
所以这不是条件语句,它只是一个用作整数的布尔值。