研究日誌。

大規模なグラフ処理に対してメモリ階層構造を考慮した高性能なソフトウェアを開発。

行列積とループ・ブロッキング。

2007-10-19 16:56:45 | Weblog
インテル日本語資料の「IA-32 インテルアーキテクチャー最適化」にも記されている、ループ・ブロッキングの有効性を検証してみた。
ループ・ブロッキングとは、キャッシュサイズを考慮してループをブロックすることである。
反復のたびにおきてしまうキャッシュミスを軽減することができ、キャッシュとメモリーのパフォーマンスを向上させることができる。

実験環境は Xeon 3.0GHz であるが、今回はスレッド数は1に固定した。ちなみにキャッシュサイズは 4096 KB となっている。
今回は 512 x 512 と 1024 x 1024 の2種類のみであるが、次の4パターンについてプロファイルしてみた。
(0) GotoBLAS (あくまでも参考)
(1) 通常の積演算
(2) 通常の積演算にループ・ブロッキングを適用
(3) 事前に行列Bに対して転置処理を行い、アクセス方向を修正した積演算
(4) 事前に行列Bに対して転置処理を行い、アクセス方向を修正した積演算に対してループ・ブロッキングを適用


ループの大きさによってかなりのバラツキがあることにおどろいた。
通常のループであれば、ブロックサイズは2~8程度と小さめで、
事前にアクセス方向を修正しておけば、256と大きめがよいようだ。


512 x 512
    1   2  4   8   16  32  64   128  256   512
(0)  35
(1) 1700
(2) 1500 765 825 1000 1327 1400 1420 1450 1450 1500
(3)  190
(4)  920 365 257  228  211 200  196  202 198  196


1024 x 1024
    1    2    4   8   16   32   64   128   256   512  1024
(0) 235
(1) 60000
(2) 65000 23300 10700 9600 11200 11500 11500 11800 11700 17500 54500
(3) 2550
(4) 7600  3050  2120 1800  1710 1625  1580  1626 1610  2050  2420


以下はソースコードの一部である。


 /* (1)通常の積演算 */
 for (m = 0; m < M; m++) {
  for (n = 0; n < N; n++) {
   for (k = 0; k < K; k++) {
    C[m * N + n] += A[m * K + k] * B[k * N + n];
   }
  }
 }


 /* (2)通常の積演算にループ・ブロッキングを適用 */
 for (m = 0; m < M; m+=block) {
  for (n = 0; n < N; n+=block) {
   for (k = 0; k < K; k+=block) {
    
    for (mm = m; mm < m+block; mm++) {
     for (nn = n; nn < n+block; nn++) {
      for (kk = k; kk < k+block; kk++) {
       C[mm * N + nn] += A[mm * K + kk] * B[kk * N + nn];
      }
     }
    }
    
   }
  }
 }

 /* (3)事前に行列Bに対して転置処理を行い、アクセス方向を修正した積演算 */
 for (m = 0; m < M; m++) {
  for (n = 0; n < N; n++) {
   for (k = 0; k < K; k++) {
    C[m * N + n] += A[m * K + k] * B[n * K + k];
   }
  }
 }

 /* (4)事前に行列Bに対して転置処理を行い、
   アクセス方向を修正した積演算に対してループ・ブロッキングを適用 */
 for (m = 0; m < M; m+=block) {
  for (n = 0; n < N; n+=block) {
   for (k = 0; k < K; k+=block) {
    
    for (mm = m; mm < m+block; mm++) {
     for (nn = n; nn < n+block; nn++) {
      for (kk = k; kk < k+block; kk++) {
       C[mm * N + nn] += A[mm * K + kk] * B[nn * K + kk];
      }
     }
    }
    
   }
  }
 }