【发布时间】:2014-10-27 10:55:27
【问题描述】:
我注意到我的代码在 64 位 Linux 上运行比在 32 位 Linux 或 64 位 Window 或 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 建议)修复了两个GCC 版本的-m32 -std=c99。我仍在寻找 gcc-4.8 上 64 位的解决方案,因为它是现在比 4.9 更常见的版本。
【问题讨论】:
-
auto-vectorization 失败的 3 倍提示。看看 -S 程序集清单。
-
@HansPassant 否,“矢量化由标志 -ftree-vectorize 启用,默认为 -O3”。
-
在没有 gcc 优化的情况下运行程序时,我得到了〜执行时间(用户:0m14.349s(x86)VS 0m14.723s(x86_64))。 GCC 优化失败?!
-
与问题无关,但为了提供信息:你不能定义一个名为
stretch()的函数,所有以str开头的公共函数名称都是保留的。 -
您是否检查过为 64 位架构编译的 libc6 版本是否用于 64 位版本的编译?
标签: c performance gcc 64-bit