2

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;
}
4

1 に答える 1

1

カフから:

  • 厳密なエイリアシング規則がない場合、コンパイラは、意図したものとは微妙に異なることを行う最適化されたコードを生成する可能性があります。

  • 厳密なエイリアシング ルールを無効にするとコードが高速になるとは限りません。

  • その場合、最適化されたコードが実際に異なる結果を示すことも当然ではありません。これは、実際のデータ アクセス パターンに大きく依存し、多くの場合、プロセッサ/キャッシュ アーキテクチャにも依存します。

サンプルコードに関しては、関数内の配列要素への書き込みアクセスは決してないため、エイリアシングは(少なくとも発行されたコードの場合)無関係sumXXXであると言えます。

(同じベクトルを 2 回渡すと、パフォーマンスがわずかに向上する (または逆になる) 可能性があります。ホット キャッシュと小さいキャッシュ フットプリントによる恩恵が得られる可能性があります。プリフェッチ プレディクタをオフトラックにする冗長なロードによるペナルティが発生する可能性があります。いつものように) :プロファイラを使用)

于 2012-09-28T06:55:21.580 に答える