C/C++:正在使用比较结果作为int真正的无分支
C/C++ : is using the result of comparison as int really branchless?
我在许多SO答案中看到过这种代码:
template <typename T>
inline T imax (T a, T b)
{
return (a > b) * a + (a <= b) * b;
}
哪里的作者说这是无枝的。
但这真的是当前架构的无分支吗?(x86、ARM…)有没有一个真正的标准保证这是无分支的?
x86有SETcc
系列指令,根据标志的值将字节寄存器设置为1或0。编译器通常使用它来实现这种没有分支的代码。
如果你使用"天真"的方法
int imax(int a, int b) {
return a > b ? a : b;
}
编译器将使用CMOVcc
(条件移动)指令族生成更高效的无分支代码。
ARM能够有条件地执行每一条指令,使编译器能够高效地编译您的和幼稚的实现,幼稚的实现更快。
我偶然发现了这个SO问题,因为我问了我同样的问题……事实证明并不总是这样。例如,以下代码…
const struct op {
const char *foo;
int bar;
int flags;
} ops[] = {
{ "foo", 5, 16 },
{ "bar", 9, 16 },
{ "baz", 13, 0 },
{ 0, 0, 0 }
};
extern int foo(const struct op *, int);
int
bar(void *a, void *b, int c, const struct op *d)
{
c |= (a == b) && (d->flags & 16);
return foo(d, c) + 1;
}
…在所有优化级别中使用gcc 3.4.6(i386)和8.3.0(amd64,i386)编译为分支代码。3.4.6中的那个更容易手动阅读,我将用gcc -O2 -S -masm=intel x.c; less x.s
:进行演示
[…]
.text
.p2align 2,,3
.globl bar
.type bar , @function
bar:
push %ebp
mov %ebp, %esp
push %ebx
push %eax
mov %eax, DWORD PTR [%ebp+12]
xor %ecx, %ecx
cmp DWORD PTR [%ebp+8], %eax
mov %edx, DWORD PTR [%ebp+16]
mov %ebx, DWORD PTR [%ebp+20]
je .L4
.L2:
sub %esp, 8
or %edx, %ecx
push %edx
push %ebx
call foo
inc %eax
mov %ebx, DWORD PTR [%ebp-4]
leave
ret
.p2align 2,,3
.L4:
test BYTE PTR [%ebx+8], 16
je .L2
mov %cl, 1
jmp .L2
.size bar , . - bar
指针比较操作调用了一个比较,甚至调用了一个子例程来插入1。
即使不使用!!(a == b)
也会带来不同。
tl;dr
检查实际编译的实际编译器输出(使用-S
进行汇编或使用objdump -d -Mintel x.o
进行反汇编;如果不在x86上,则删除-Mintel
,这只会使程序集更清晰);编译器是变幻莫测的野兽。
相关文章:
- 是否可以将无符号 int 的最大值转换为 int 并将结果转换为 -1?
- 什么是在C 中存储结果INT*的最佳数据类型
- 为什么这个条件运算符的计算结果为 int?
- 如果我将未签名的int添加到负int且算术结果是正面的,会发生什么
- 为什么 std::sort 默认假定 std::vector< std::vector<int> >为 std::vector,从而产生错误的结果?
- (int)-2147483648的结果使用不同的编译器除以(int)-1
- 为什么字符<<和字符>>中间结果 int
- "Int" C++ 中的乘法"long long"结果
- 带有unsigned int参数的C++函数在使用negative调用它时会得到奇怪的结果
- 比较INT会产生奇怪的结果
- 当我们在int上进行操作时,结果是暂时存储在int中的结果
- 将字符串转换为 int 并将结果存储在变量中
- 将双精度转换为 int 的结果是错误的
- 一个关于C++中double和int之间转换的奇怪结果
- 使用 sstream 将 int 转换为 LPCTSTR 时出现奇怪的结果
- C++字符和 int 之间的转换,结果错误
- C/C++:正在使用比较结果作为int真正的无分支
- "Truncation from int to char"不产生任何结果
- 将-1移位到sizeof(int)在C++中生成两个不同的结果
- 从类型char到int的转换会产生不同的结果