【发布时间】:2010-01-11 02:22:08
【问题描述】:
以下是在 x86-64 上的 C 中设置单个位的两种方法:
inline void SetBitC(long *array, int bit) {
//Pure C version
*array |= 1<<bit;
}
inline void SetBitASM(long *array, int bit) {
// Using inline x86 assembly
asm("bts %1,%0" : "+r" (*array) : "g" (bit));
}
使用带有 -O3 -march=core2 选项的 GCC 4.3,当使用常量 bit 时,C 版本需要大约 90% 的时间。 (两个版本编译成完全相同的汇编代码,只是 C 版本使用or [1<<num],%rax 指令而不是bts [num],%rax 指令)
当与变量 bit 一起使用时,C 版本的性能更好,但仍然比内联汇编慢很多。
重置、切换和检查位具有相似的结果。
为什么 GCC 对这种常见操作的优化如此糟糕?我在 C 版本上做错了吗?
编辑:抱歉让您久等了,这是我用来进行基准测试的代码。它实际上是从一个简单的编程问题开始的......
int main() {
// Get the sum of all integers from 1 to 2^28 with bit 11 always set
unsigned long i,j,c=0;
for (i=1; i<(1<<28); i++) {
j = i;
SetBit(&j, 10);
c += j;
}
printf("Result: %lu\n", c);
return 0;
}
gcc -O3 -march=core2 -pg test.c
./a.out
gprof
with ASM: 101.12 0.08 0.08 main
with C: 101.12 0.16 0.16 main
time ./a.out 也给出了类似的结果。
【问题讨论】:
-
真的是“普通操作”吗?我看到使用位操作的最常见情况是人们认为他们会通过将一堆标志打包成一个字节来节省内存的过早优化。
-
嗯...好点。尽管如此,这是一个非常简单的操作,并且在硬件驱动程序中很常见。无论如何,关键是性能下降了 90%。
-
在驱动程序编程中,我很确定“左移 1 来确定我们的掩码”成语使用得不多。相反,您
or带有预定义的标志。 -
你是对的。当位为常数时,编译器会优化移位。真的很奇怪,它仍然慢了 90%。
-
我对“慢 90%”的事情感到惊讶。编译器可能期望
or比bts占用更少的时钟——这通常是正确的。
标签: c optimization assembly x86