在GCC C99中严格别名指针的例子,没有性能差异

时间:2012-09-28 06:40:44

标签: performance gcc c99 aliasing

我试图了解严格别名对C99性能的影响。我的目标是优化矢量点积,这会占用我程序中的大量时间(描述它!)。我认为别名可能是问题,但是下面的代码没有显示标准方法和严格别名版本之间的任何实质性差异,即使是大小为1亿的向量。我也尝试使用局部变量来避免混叠,结果相似。

发生了什么事?

我在OSX 10.7.4上使用gcc-4.7。结果以微秒为单位。

$ /usr/local/bin/gcc-4.7 -fstrict-aliasing -Wall -std=c99 -O3 -o restrict restrict.c
$ ./restrict
sum:    100000000   69542
sum2:   100000000   70432
sum3:   100000000   70372
sum4:   100000000   69891
$ /usr/local/bin/gcc-4.7 -Wall -std=c99 -O0 -fno-strict-aliasing -o restrict restrict.c
$ ./restrict
sum:    100000000   258487
sum2:   100000000   261349
sum3:   100000000   258829
sum4:   100000000   258129

restrict.c(注意这段代码需要几百MB的RAM):

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

/* original */
long sum(int *x, int *y, int n)
{
   long i, s = 0;

   for(i = 0 ; i < n ; i++)
      s += x[i] * y[i];

   return s;
}

/* restrict */
long sum2(int *restrict x, int *restrict y, int n)
{
   long i, s = 0;

   for(i = 0 ; i < n ; i++)
      s += x[i] * y[i];

   return s;
}

/* local restrict */
long sum3(int *x, int *y, int n)
{
   int *restrict xr = x;
   int *restrict yr = y;
   long i, s = 0;

   for(i = 0 ; i < n ; i++)
      s += xr[i] * yr[i];

   return s;
}

/* use local variables */
long sum4(int *x, int *y, int n)
{
   int xr, yr;
   long i, s = 0;

   for(i = 0 ; i < n ; i++)
   {
      xr = x[i];
      yr = y[i];
      s += xr * yr;
   }

   return s;
}

int main(void)
{
   struct timeval tp1, tp2;
   struct timezone tzp;

   long i, n = 1e8L, s;
   int *x = malloc(sizeof(int) * n);
   int *y = malloc(sizeof(int) * n);
   long elapsed1;

   for(i = 0 ; i < n ; i++)
      x[i] = y[i] = 1;

   gettimeofday(&tp1, &tzp);
   s = sum(x, y, n);
   gettimeofday(&tp2, &tzp);
   elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6
      + (tp2.tv_usec - tp1.tv_usec);
   printf("sum:\t%ld\t%ld\n", s, elapsed1);

   gettimeofday(&tp1, &tzp);
   s = sum2(x, y, n);
   gettimeofday(&tp2, &tzp);
   elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6
      + (tp2.tv_usec - tp1.tv_usec);
   printf("sum2:\t%ld\t%ld\n", s, elapsed1);

   gettimeofday(&tp1, &tzp);
   s = sum3(x, y, n);
   gettimeofday(&tp2, &tzp);
   elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6
      + (tp2.tv_usec - tp1.tv_usec);
   printf("sum3:\t%ld\t%ld\n", s, elapsed1);

   gettimeofday(&tp1, &tzp);
   s = sum3(x, y, n);
   gettimeofday(&tp2, &tzp);
   elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6
      + (tp2.tv_usec - tp1.tv_usec);
   printf("sum4:\t%ld\t%ld\n", s, elapsed1);

   return EXIT_SUCCESS;
}

1 个答案:

答案 0 :(得分:1)

脱下袖口:

  • 没有严格的别名规则,编译器 可能 只会生成优化的代码,这些代码与预期的内容略有不同。

  • 禁用严格别名规则导致代码更快,这不是一个假设。

  • 如果确实如此,那么 并非优化代码实际上显示不同的结果。这在很大程度上取决于实际的数据访问模式,甚至通常是处理器/缓存架构。

关于 您的示例代码,我会说别名 无关 (对于发出的代码,at至少)因为sumXXX函数内的数组元素永远不会有任何写访问权。

(如果你将相同的向量传递两次,你可能会获得更好的性能(或相反)。热缓存和较小的缓存占用可能会有一个好处。冗余的负载可能会有一个惩罚,因为预取预测器偏离轨道一如既往: 使用分析器