编译器嵌套循环优化,用于顺序内存访问。

时间:2012-02-28 05:38:06

标签: c++ performance gcc loops compiler-construction

我在矩阵乘法基准测试中遇到了一个奇怪的性能问题(来自MOSBENCH套件的Metis中的matrix_mult)。优化基准以平铺数据,使得活动工作集为12kb(3个32x32英寸的图块)并且适合L1缓存。长话短说,交换内部两个大多数循环在某些阵列输入大小(4096,8192)上的性能差异几乎是4倍,而在其他阵列输入大小上差异大约为30%。问题基本上归结为顺序访问元素而不是步幅模式。我认为某些数组大小创建了一个糟糕的步幅访问,产生了很多缓存线冲突。当从双向关联L1变为8向关联L1时,性能差异明显减小。

我的问题是为什么gcc不优化循环排序以最大化顺序内存访问?

下面是问题的简化版本(请注意,性能时间高度依赖于L1配置。下面显示的数字来自2.3 GHZ AMD系统,其中64K L1 2路关联编译为-O3)。

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];

2 个答案:

答案 0 :(得分:1)

  1. gcc可能无法证明指针不重叠。如果您使用非标准扩展程序,则可以尝试使用__restrict
  2. gcc没有充分利用您的体系结构,以避免为每个处理器重新编译的必要性。使用具有适当系统值的-march选项可能会有所帮助。

答案 1 :(得分:0)

gcc有一堆优化,可以做你想做的事。

查找-floop-strip-mine和-floop-block编译器选项。

手册中的引用:

  

对循环执行循环阻塞转换。堵塞地雷   循环嵌套中的每个循环使得内存访问   元素循环适合缓存。可以使用更改条带长度   loop-block-tile-size参数。