問題は、cublas などはすべての SM を使用して大きな行列を乗算するように設計されていることです。それはあなたが望むものではありません。小さな行列の乗算をたくさんしたい場合。
これを CUBLAS でうまく処理できるものにキャストする方法があるかもしれませんが、私にはわかりません。私の提案は次のとおりです。
1 つのスレッド ブロックを使用して 2 つの小さな行列を乗算し、結果を出力するカーネルを作成します。
次に、大量のブロックを含むカーネル ログ2 N を起動し、ペアごとに乗算に取り組みます。
- ステップ 1: M 1 x M 2、M 3 x M 4 ... M N - 2 x M N-1を乗算し、M' 1、M' 2 .. M' N/2を出力します。
- ステップ 2: M' 1 x M' 2 , M' 3 x M' 4 ... M' N/2 - 2 x M N/2-1を乗算し、M'' 1 ,M'' 2 .. Mを出力します。 '' N/4 ...
等
50% のメモリ オーバーヘッドが発生しますが、この方法でコアをより有効に活用できると思います。
アップデート
段階的にこれを行いたくない場合は、この方法で行うこともできますが、より多くのコーディングが必要になり、cuBLAS や非同期転送などで得られるものと比較して、おそらくパフォーマンスが低下します。Fermi を使用していて、L1 キャッシュをオフにしたため、SM ごとに 48K の共有メモリがあると仮定しています。
100 個の行列を 2x2 ブロック形式で格納し、各ブロックはメモリ内で連続しています。で始まりmatrix[matrixnum,i,j]
ますmatricies[matrixnum*100*100 + i*100*50 + j*50*50]
。各ブロックは 50*50*4 バイト ~ 10K であるため、共有メモリには 4 つが快適に収まることに注意してください。
乗算する行列の (Nmatricies/Nblocks) の長さのチェーンを 4 つのスレッドブロックごとに割り当てます。4 つのうちの 1 つが乗算の各ブロックを担当します。
あなたがスレッドブロック 1/4 で、乗算する最初の行列が AxB であるとしましょう。あなたは結果の (1,1) - (AB) 1,1 = A 1,1 B 1,1 + A 1,2 *B 2,1に責任があります。A 1,1を共有メモリの myblock[0] にプリロードします。
- グローバル メモリからmyblock[1] = B 1,1にロードする
- myblock[3] = myblock[0] * myblock[1] (行列 mult、すべて共有メモリ内)
- load in myblock[1] =グローバルからの A 1,2
- myblock[2] にロード =グローバルからB 2,1
- myblock[0] = myblock[3] + (myblock[1] * myblock[2]) (行列の乗算と加算、すべて共有メモリ内)。
これで、チェーンの一部の行列の残りのシーケンスに対してこれを繰り返すことができ、完了した場合にのみ出力されます。
完了すると、グローバル メモリ内に (#SMs) 行列ができてしまいますが、これはまだ乗算する必要がありますが、グローバル メモリ内に追加の一時ストレージは存在せず、乗算する必要もありません。元のマトリックスと取り組むべきもののリスト以外のデータをグローバルメモリにコピーします。
繰り返しますが、これを行う本当の理由はありませんが、データを段階的に GPU に送信する必要がなく、パフォーマンスがほぼ確実に低下します。グローバル メモリへの書き込みは少なくなりますが、その代償として手巻きの GEMM を使用することになるでしょう。幸いなことに、50 は 8 の倍数ではないため、共有メモリ バンクの競合があまり発生しない可能性があります。
繰り返しになりますが、ボーナス ポイントとして、最初にすべての対行列積のすべてのブロックを事前計算し、次にリストの長さの半分を事前計算できます。