C++ 用于顺序内存访问的编译器嵌套循环优化。
我在matrix乘法基准测试中遇到了一个奇怪的性能问题(matrix_mult在Metis中来自套件)。对基准测试进行了优化,以平铺数据,从而使活动工作集为12kb(3个32x32整数的平铺),并适合一级缓存。长话短说,在某些阵列输入大小(40968192)上,交换内部两个most循环的性能差异几乎是4倍,而在其他阵列输入大小上的性能差异约为30%。问题本质上归结为按顺序访问元素,而不是按步幅模式访问元素。我认为某些数组大小造成了错误的跨步访问,从而产生了大量缓存线冲突。从双向关联L1更改为8向关联L1时,性能差异明显减小 我的问题是为什么gcc不优化循环顺序以最大化顺序内存访问 下面是该问题的简化版本(请注意,性能时间高度依赖于L1配置。下面所示的数字来自2.3 GHZ AMD系统,64K L1双向关联编译为-O3)C++ 用于顺序内存访问的编译器嵌套循环优化。,c++,performance,gcc,loops,compiler-construction,C++,Performance,Gcc,Loops,Compiler Construction,我在matrix乘法基准测试中遇到了一个奇怪的性能问题(matrix_mult在Metis中来自套件)。对基准测试进行了优化,以平铺数据,从而使活动工作集为12kb(3个32x32整数的平铺),并适合一级缓存。长话短说,在某些阵列输入大小(40968192)上,交换内部两个most循环的性能差异几乎是4倍,而在其他阵列输入大小上的性能差异约为30%。问题本质上归结为按顺序访问元素,而不是按步幅模式访问元素。我认为某些数组大小造成了错误的跨步访问,从而产生了大量缓存线冲突。从双向关联L1更改为8向
N=ARRAY\u SIZE//1024
int*mat_A=(int*)malloc(N*N*sizeof(int));
int*mat_B=(int*)malloc(N*N*sizeof(int));
int*mat_C=(int*)malloc(N*N*sizeof(int));
//mat_B元素以长度为N的步幅模式访问
//这需要800毫秒
对于(int t=0;t<1000;t++)
对于(int a=0;a<32;a++)
对于(int b=0;b<32;b++)
对于(int c=0;c<32;c++)
mat_C[N*a+b]+=mat_a[N*a+C]*mat_b[N*C+b];
//内部两个循环交换
//元素现在在内部循环中按顺序访问
//这需要172毫秒
对于(int t=0;t<1000;t++)
对于(int a=0;a<32;a++)
对于(int c=0;c<32;c++)
对于(int b=0;b<32;b++)
mat_C[N*a+b]+=mat_a[N*a+C]*mat_b[N*C+b];
gcc有一系列的优化,只是做你想要的 查找-floop strip mine和-floop block编译器选项 引自手册: 对循环执行循环阻塞转换。封闭露天矿 循环中的每个循环嵌套,以便 元素循环适合缓存内部。条带长度可通过以下方式更改: 循环块平铺大小参数
因为很难向编译器证明这样的更改不会改变操作的语义。也许您会对google gcc+graphite感兴趣,它是基于多面体模型的循环转换的一个合并分支。在某个地方有一个可能的转换列表。我认为,由于整数加法是可交换的,因此对于编译器来说,证明该操作对循环器顺序是不变的是相当简单的。我想知道代码示例是什么使得优化变得不平凡。值得注意的是,限制是C99的标准,而不是C++。另请参见@torek,我知道,只是想说明它是一个非标准的扩展,但受到许多编译器的支持。谢谢,虽然问题不是由于缓存中不适合内部循环,因为优化已经在代码中手动完成。内部循环只有12kb的数据占用空间。3*1024个整数值。
N = ARRAY_SIZE // 1024
int* mat_A = (int*)malloc(N*N*sizeof(int));
int* mat_B = (int*)malloc(N*N*sizeof(int));
int* mat_C = (int*)malloc(N*N*sizeof(int));
// Elements of mat_B are accessed in a stride pattern of length N
// This takes 800 msec
for (int t = 0; t < 1000; t++)
for (int a = 0; a < 32; a++)
for (int b = 0; b < 32; b++)
for (int c = 0; c < 32; c++)
mat_C[N*a+b] += mat_A[N*a+c] * mat_B[N*c+b];
// Inner two loops are swapped
// Elements are now accessed sequentially in inner loop
// This takes 172 msec
for (int t = 0; t < 1000; t++)
for (int a = 0; a < 32; a++)
for (int c = 0; c < 32; c++)
for (int b = 0; b < 32; b++)
mat_C[N*a+b] += mat_A[N*a+c] * mat_B[N*c+b];