我注意到我的代码在64位Linux上运行比在32位Linux或64位Windows或64位Mac上慢得多。 这是一个最小化的测试案例。
#include <stdlib.h>
typedef unsigned char UINT8;
void
stretch(UINT8 * lineOut, UINT8 * lineIn, int xsize, float *kk)
{
int xx, x;
for (xx = 0; xx < xsize; xx++) {
float ss = 0.0;
for (x = 0; x < xsize; x++) {
ss += lineIn[x] * kk[x];
}
lineOut[xx] = (UINT8) ss;
}
}
int
main( int argc, char** argv )
{
int i;
int xsize = 2048;
UINT8 *lineIn = calloc(xsize, sizeof(UINT8));
UINT8 *lineOut = calloc(xsize, sizeof(UINT8));
float *kk = calloc(xsize, sizeof(float));
for (i = 0; i < 1024; i++) {
stretch(lineOut, lineIn, xsize, kk);
}
return 0;
}
以下是它的运行方式:
$ cc --version
cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
$ cc -O2 -Wall -m64 ./tt.c -o ./tt && time ./tt
user 14.166s
$ cc -O2 -Wall -m32 ./tt.c -o ./tt && time ./tt
user 5.018s
正如您所看到的,32位版本运行速度几乎快了3倍(我在32位和64位Ubuntu上进行了测试,结果相同)。更奇怪的是性能取决于C标准:
$ cc -O2 -Wall -std=c99 -m32 ./tt.c -o ./tt && time ./tt
user 15.825s
$ cc -O2 -Wall -std=gnu99 -m32 ./tt.c -o ./tt && time ./tt
user 5.090s
这怎么可能?我该如何解决这个问题,以加速由GCC生成的64位版本。
更新1
我比较了快速32位(默认和gnu99)和慢速(c99)产生的汇编代码,发现以下问题:
.L5:
movzbl (%ebx,%eax), %edx # MEM[base: lineIn_10(D), index: _72, offset: 0B], D.1543
movl %edx, (%esp) # D.1543,
fildl (%esp) #
fmuls (%esi,%eax,4) # MEM[base: kk_18(D), index: _72, step: 4, offset: 0B]
addl $1, %eax #, x
cmpl %ecx, %eax # xsize, x
faddp %st, %st(1) #,
fstps 12(%esp) #
flds 12(%esp) #
jne .L5 #,
在快速情况下,没有fstps
和flds
命令。因此,GCC在每个步骤中从内存中存储和加载值。我尝试了register float
类型,但这并没有帮助。
更新2
我已经在gcc-4.9上进行了测试,看起来它为64位生成了最佳代码。而-ffast-math
(由@jch建议)修复了-m32 -std=c99
对于两个GCC版本。我仍在寻找解决方案,以便在gcc-4.8上实现64位,因为现在它比4.9更常见。