【发布时间】:2013-03-24 02:20:58
【问题描述】:
我有一个看起来像这样的Compare() 函数:
inline bool Compare(bool greater, int p1, int p2) {
if (greater) return p1>=p2;
else return p1<=p2;
}
我决定优化以避免分支:
inline bool Compare2(bool greater, int p1, int p2) {
bool ret[2] = {p1<=p2,p1>=p2};
return ret[greater];
}
然后我通过这样做进行了测试:
bool x = true;
int M = 100000;
int N = 100;
bool a[N];
int b[N];
int c[N];
for (int i=0;i<N; ++i) {
a[i] = rand()%2;
b[i] = rand()%128;
c[i] = rand()%128;
}
// Timed the below loop with both Compare() and Compare2()
for (int j=0; j<M; ++j) {
for (int i=0; i<N; ++i) {
x ^= Compare(a[i],b[i],c[i]);
}
}
结果:
Compare(): 3.14ns avg
Compare2(): 1.61ns avg
我会说案例关闭,避免分支 FTW。但是为了完整起见,我替换了
a[i] = rand()%2;
与:
a[i] = true;
得到了完全相同的测量结果,约为 3.14ns。据推测,那时没有进行分支,编译器实际上正在重写Compare() 以避免if 语句。但是,为什么Compare2() 更快呢?
不幸的是,我是汇编代码文盲,否则我会尝试自己回答这个问题。
编辑:下面是一些程序集:
_Z7Comparebii:
.LFB4:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movl %edi, %eax
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movb %al, -4(%rbp)
cmpb $0, -4(%rbp)
je .L2
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setge %al
jmp .L3
.L2:
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setle %al
.L3:
leave
ret
.cfi_endproc
.LFE4:
.size _Z7Comparebii, .-_Z7Comparebii
.section .text._Z8Compare2bii,"axG",@progbits,_Z8Compare2bii,comdat
.weak _Z8Compare2bii
.type _Z8Compare2bii, @function
_Z8Compare2bii:
.LFB5:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movl %edi, %eax
movl %esi, -24(%rbp)
movl %edx, -28(%rbp)
movb %al, -20(%rbp)
movw $0, -16(%rbp)
movl -24(%rbp), %eax
cmpl -28(%rbp), %eax
setle %al
movb %al, -16(%rbp)
movl -24(%rbp), %eax
cmpl -28(%rbp), %eax
setge %al
movb %al, -15(%rbp)
movzbl -20(%rbp), %eax
cltq
movzbl -16(%rbp,%rax), %eax
leave
ret
.cfi_endproc
.LFE5:
.size _Z8Compare2bii, .-_Z8Compare2bii
.text
现在,执行测试的实际代码可能正在使用上述两个函数的内联版本,因此这可能是要分析的错误代码。话虽如此,我在Compare() 中看到了一个jmp 命令,所以我认为这意味着它正在分支。如果是这样,我想这个问题就变成了:当我将a[i] 从rand()%2 更改为true(或false)时,为什么分支预测器不能提高Compare() 的性能?
EDIT2:我将“分支预测”替换为“分支”以使我的帖子更合理。
【问题讨论】:
-
optimize to avoid branch prediction这不是矛盾吗? -
您必须共享汇编代码,因为发生的情况很大程度上取决于您使用的编译器以及优化级别。
-
@Last Line:那你为什么不发布程序集呢?
-
你没有设置种子。也许编译器足够聪明,知道
rand()在这种情况下返回什么?只是一个快速的想法。另外,您应该真正比较程序集。即使你是汇编代码文盲,你仍然可以展示差异。 -
可能是有条件的移动.. 显示程序集。
标签: c++ optimization branch-prediction