【发布时间】:2015-08-09 18:56:59
【问题描述】:
是否有 gcc pragma 或我可以用来强制 gcc 在特定代码部分生成无分支指令的东西?
我有一段代码希望 gcc 使用 cmov 指令编译成无分支代码:
int foo(int *a, int n, int x) {
int i = 0, j = n;
while (i < n) {
#ifdef PREFETCH
__builtin_prefetch(a+16*i + 15);
#endif /* PREFETCH */
j = (x <= a[i]) ? i : j;
i = (x <= a[i]) ? 2*i + 1 : 2*i + 2;
}
return j;
}
确实如此:
morin@soprano$ gcc -O4 -S -c test.c -o -
.file "test.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
testl %esi, %esi
movl %esi, %eax
jle .L2
xorl %r8d, %r8d
jmp .L3
.p2align 4,,10
.p2align 3
.L6:
movl %ecx, %r8d
.L3:
movslq %r8d, %rcx
movl (%rdi,%rcx,4), %r9d
leal (%r8,%r8), %ecx # put 2*i in ecx
leal 1(%rcx), %r10d # put 2*i+1 in r10d
addl $2, %ecx # put 2*i+2 in ecx
cmpl %edx, %r9d
cmovge %r10d, %ecx # put 2*i+1 in ecx if appropriate
cmovge %r8d, %eax # set j = i if appropriate
cmpl %esi, %ecx
jl .L6
.L2:
rep ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
(是的,我意识到循环是一个分支,但我说的是循环内的选择运算符。)
不幸的是,当我启用__builtin_prefetch 调用时,gcc 会生成分支代码:
morin@soprano$ gcc -DPREFETCH -O4 -S -c test.c -o -
.file "test.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
testl %esi, %esi
movl %esi, %eax
jle .L7
xorl %ecx, %ecx
jmp .L5
.p2align 4,,10
.p2align 3
.L3:
movl %ecx, %eax # this is the x <= a[i] branch
leal 1(%rcx,%rcx), %ecx
cmpl %esi, %ecx
jge .L11
.L5:
movl %ecx, %r8d # this is the main branch
sall $4, %r8d # setup the prefetch
movslq %r8d, %r8 # setup the prefetch
prefetcht0 60(%rdi,%r8,4) # do the prefetch
movslq %ecx, %r8
cmpl %edx, (%rdi,%r8,4) # compare x with a[i]
jge .L3
leal 2(%rcx,%rcx), %ecx # this is the x > a[i] branch
cmpl %esi, %ecx
jl .L5
.L11:
rep ret
.L7:
.p2align 4,,5
rep ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
我试过在这个函数上使用__attribute__((optimize("if-conversion2"))),但是没有效果。
我如此关心的原因是我手工编辑了编译器生成的无分支代码(来自第一个示例)以包含 prefetcht0 指令,它的运行速度比 gcc 生成的两个版本快得多。
【问题讨论】:
-
你编译的优化级别是多少?因为当我编写普通代码并使用 -O3 或 -Ofast 时,我很难击败编译器
-
我猜你可以提示 gcc 条件是真还是假的概率。看起来您的 gcc 非常确信该分支要么大部分为真,要么大部分为假,因此运行时预测比预测效果更好?
-
@GradyPlayer:这是用 -O4 编译的,在第一个例子中它愉快地使用了 cmov 操作。
-
@user3528438 :在这种情况下,gcc 是完全错误的。每个分支在每一步都有 50% 的机会被采用。
-
显然,这并不是特定于预取的。只需添加例如
asm ("");代替__builtin_prefetch具有相同的效果。
标签: c gcc optimization x86