我一直试图通过使用以下代码对一个 routine 进行计时,从而了解将数组放在一级缓存和内存中会产生什么影响(我知道我应该在最后用‘a’对结果进行zoom ;重点是在循环内既做乘法又做加法-到目前为止,编译器还没有计算出‘a’的因子):
double sum(double a,double* X,int size)
{
double total = 0.0;
for(int i = 0; i < size; ++i)
{
total += a*X[i];
}
return total;
}
#define KB 1024
int main()
{
//Approximately half the L1 cache size of my machine
int operand_size = (32*KB)/(sizeof(double)*2);
printf("Operand size: %d\n", operand_size);
double* X = new double[operand_size];
fill(X,operand_size);
double seconds = timer();
double result;
int n_iterations = 100000;
for(int i = 0; i < n_iterations; ++i)
{
result = sum(3.5,X,operand_size);
//result += rand();
}
seconds = timer() - seconds;
double mflops = 2e-6*double(n_iterations*operand_size)/seconds;
printf("Vector size %d: mflops=%.1f, result=%.1f\n",operand_size,mflops,result);
return 0;
}
请注意,为了简洁起见,不包括timer()和fill() routine ;如果您想运行代码,可以在此处找到它们的完整源代码:
现在,这里是它变得有趣的地方.这是输出:
Operand size: 2048
Vector size 2048: mflops=588.8, result=-67.8
这是完全未缓存的性能,尽管在循环迭代之间X的所有元素都应该被保存在缓存中.查看以下程序生成的汇编代码:
g++ -O3 -S -fno-asynchronous-unwind-tables register_opt_example.cpp
我注意到SUM函数循环中的一个奇怪之处:
L55:
movsd (%r12,%rax,8), %xmm0
mulsd %xmm1, %xmm0
addsd -72(%rbp), %xmm0
movsd %xmm0, -72(%rbp)
incq %rax
cmpq $2048, %rax
jne L55
说明如下:
addsd -72(%rbp), %xmm0
movsd %xmm0, -72(%rbp)
指示它在堆栈上存储sum()中的"total"值,并在每次循环迭代时读取和写入该值.我修改了程序集,以便将此操作数保存在a寄存器中:
...
addsd %xmm0, %xmm3
...
这一小小的变化带来了huge%的性能提升:
Operand size: 2048
Vector size 2048: mflops=1958.9, result=-67.8
tl;dr个 我的问题是:既然单个内存位置应该存储在L1缓存中,为什么用寄存器替换单个内存位置访问会使代码速度如此之快?是什么架构因素使这成为可能?重复写入一个堆栈位置会完全 destruct 缓存的有效性,这似乎非常奇怪.
Appendix
我的gcc版本是:
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646.1~2/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646) (dot 1)
我的CPU是:
英特尔至强X5650