前提・実現したいこと
Netlibのサイト上のBLASのdgemm関数ををC言語から呼び出して行列積を計算し、性能比較を行ったのですが、本来BLASを用いたものの方が性能が良くなるはずですが、自分の書いたコードの方が良い性能を出してしまいました。「BLASを用いたコード」の方の誤りを指摘していただきたいです。
発生している問題・エラーメッセージ
以下の表の通り、自分で最適化したコードの方が性能が良くなってしまいます。
性能計算には、行列サイズN、計測時間t[s]として
MFlops = 2n^3 / t
で定義されるMFlopsという値を用いました。
行列サイズ | 自分で書いたコードの性能 | BLASの性能 |
---|---|---|
1000 | 4266.67 | 2000 |
1100 | 4259.2 | 1936 |
1200 | 3949.71 | 2067.140187 |
1300 | 4260.85 | 2008.685714 |
1400 | 4231.71 | 1973.213483 |
1500 | 4500 | 1963.636364 |
1600 | 4096 | 2032.124031 |
1700 | 4249.08 | 1894.168675 |
1800 | 4496.96 | 1852.347395 |
1900 | 4456.61 | 1817.706004 |
2000 | 4551.11 | 1865.209472 |
2100 | 4374.2 | 1863.849057 |
2200 | 4340.59 | 1831.913978 |
2300 | 4338.09 | 1830.054054 |
2400 | 4243.34 | 1938.085433 |
2500 | 4366.81 | 1913.875598 |
2600 | 4285.2 | 1973.445614 |
2700 | 4321.48 | 2036.721099 |
2800 | 4383.55 | 1975.988748 |
2900 | 4421.8 | 2007.58328 |
3000 | 4330.83 | 2003.478261 |
該当のソースコード
自分で最適化したコード
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <time.h> 4 5#define N 3000 // Max matrix size 6#define M 50 // Block size 7 8double A[N][N], B[N][N], C[N][N]; 9 10void mm_ikj_opt(int, int, int); 11 12int main() 13{ 14 int i, j, n; 15 double t, MFlops; 16 clock_t tic, toc; 17 18 int index; 19 printf("N,t,MFlops\n"); 20 21 for(index = 1000; index <= 3000; index += 100) { // 行列サイズ1000~3000まで100おきにデータを取る 22 n = index; 23 srand(2021); 24 for(i = 0; i < n; i++) { 25 for(j = 0; j < n; j++) { 26 A[i][j] = rand(); 27 B[i][j] = rand(); 28 C[i][j] = 0.0; 29 } 30 } 31 32 tic = clock(); 33 mm_ikj_opt(n, n, n); 34 toc = clock(); 35 t = (double)(toc - tic) / CLOCKS_PER_SEC; 36 MFlops = (2.0 * n) * n * n / t / 1e+6; // 性能 37 printf("%d, %.2f, %.2f\n", n, t, MFlops); 38 } 39 return 0; 40} 41 42void mm_ikj_opt(int m, int p, int n) 43{ 44 int ii, jj, kk; 45 int i, k, j; 46 47 // ループアンローリング、ブロック化などで最適化 48 for(ii = 0; ii < m; ii += M) { 49 for(kk = 0; kk < p; kk += M) { 50 for(jj = 0; jj < n; jj += M) { 51 for(i = ii; i < ii + M; i++) { 52 for(k = kk; k < kk + M; k++) { 53 for(j = jj; j < jj + M - 4; j += 4) { // loop unrolling 54 C[i][j] += A[i][k] * B[k][j]; 55 C[i][j + 1] += A[i][k] * B[k][j + 1]; 56 C[i][j + 2] += A[i][k] * B[k][j + 2]; 57 C[i][j + 3] += A[i][k] * B[k][j + 3]; 58 } 59 } 60 } 61 } 62 } 63 } 64}
BLASを用いたコード
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <time.h> 4 5#define N 3000 // matrix size 6double A[N * N], B[N * N], C[N * N]; 7 8int main() 9{ 10 int i, j, n; 11 double t, MFlops; 12 clock_t tic, toc; 13 14 int index; 15 printf("N,t,MFlops\n"); 16 17 for(index = 1000; index <= 3000; index += 100) { 18 n = index; 19 srand(2021); 20 double alpha = 1.0, beta = 0.0; 21 for(i = 0; i < n; i++) { 22 for(j = 0; j < n; j++) { 23 int id1 = i * n + j; 24 int id2 = i + j * n; 25 A[id1] = rand(); 26 B[id2] = rand(); 27 C[id1] = 0.0; 28 } 29 } 30 31 tic = clock(); 32 dgemm_("T", "N", &n, &n, &n, &alpha, A, &n, B, &n, &beta, C, &n); // dgemmで行列積を計算 33 toc = clock(); 34 t = (double)(toc - tic) / CLOCKS_PER_SEC; 35 MFlops = (2.0 * n) * n * n / t / 1e+6; 36 printf("%d, %f, %f\n", n, t, MFlops); 37 38 } 39 return 0; 40}
試したこと
FORTRANが列方向アクセスなのに対してCは行方向アクセスなので、行方向に計算を行っていく行列Aの方を値を代入する段階で転置した状態で代入し、dgemm関数内でも行列Aを転置させて計算することで速くなると思ったが、ならなかった。
補足情報(FW/ツールのバージョンなど)
OS: Windows 11
CPU: AMD Ryzen 7 3700U with Radeon Vega Mobile Gfx 2.30 GHz
コンパイラ: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
コンパイル・実行方法
$ gcc -O2 hoge.c -lblas -o hoge $ ./hoge > hoge.csv
回答1件
あなたの回答
tips
プレビュー