[解決済み] CUDAです。ブロックサイズの倍数でない行列サイズと共有メモリによるタイル型行列乗算
質問事項
CUDAプログラミングに慣れようとしているところですが、かなり楽しい時間を過ごしています。現在見ているのは これ このPDFは、共有メモリを使用した場合と使用しない場合の行列の乗算を扱っています。両方のバージョンのフルコードがあります。 こちら . このコードは、CUDAの行列乗算のサンプルにあるものとほぼ同じです。非共有メモリ版ではブロックサイズに関係なく、任意の行列サイズで実行する機能がありますが、共有メモリ版ではブロックサイズの倍数(私は4としました、デフォルトでは16でした)の行列で動作させる必要があります。
pdfの最後に提案されている問題の1つは、共有メモリ版がブロックサイズの倍数以外でも動作できるように変更することです。これは非共有版のように単純なインデックスチェックになるかと。
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if(row > A.height || col > B.width) return;
しかし、これではうまくいきません。以下は、mainメソッド(ちょっと雑ですね、すみません)を除いた全コードで、私が多少修正したものです。
void MatMul(const Matrix A, const Matrix B, Matrix C) {
// Load A and B to device memory
Matrix d_A;
d_A.width = d_A.stride = A.width;
d_A.height = A.height;
size_t size = A.width * A.height * sizeof(float);
cudaError_t err = cudaMalloc(&d_A.elements, size);
printf("CUDA malloc A: %s\n",cudaGetErrorString(err));
err = cudaMemcpy(d_A.elements, A.elements, size, cudaMemcpyHostToDevice);
printf("Copy A to device: %s\n",cudaGetErrorString(err));
Matrix d_B;
d_B.width = d_B.stride = B.width;
d_B.height = B.height;
size = B.width * B.height * sizeof(float);
err = cudaMalloc(&d_B.elements, size);
printf("CUDA malloc B: %s\n",cudaGetErrorString(err));
err = cudaMemcpy(d_B.elements, B.elements, size, cudaMemcpyHostToDevice);
printf("Copy B to device: %s\n",cudaGetErrorString(err));
Matrix d_C;
d_C.width = d_C.stride = C.width;
d_C.height = C.height;
size = C.width * C.height * sizeof(float);
err = cudaMalloc(&d_C.elements, size);
printf("CUDA malloc C: %s\n",cudaGetErrorString(err));
dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
dim3 dimGrid((B.width + dimBlock.x - 1) / dimBlock.x, (A.height + dimBlock.y-1) / dimBlock.y);
MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);
err = cudaThreadSynchronize();
printf("Run kernel: %s\n", cudaGetErrorString(err));
// Read C from device memory
err = cudaMemcpy(C.elements, d_C.elements, size, cudaMemcpyDeviceToHost);
printf("Copy C off of device: %s\n",cudaGetErrorString(err));
// Free device memory
cudaFree(d_A.elements);
cudaFree(d_B.elements);
cudaFree(d_C.elements);
}
// Get a matrix element
__device__ float GetElement(const Matrix A, int row, int col) {
return A.elements[row * A.stride + col];
}
// Set a matrix element
__device__ void SetElement(Matrix A, int row, int col, float value) {
A.elements[row * A.stride + col] = value;
}
// Get the BLOCK_SIZExBLOCK_SIZE sub-matrix Asub of A that is
// located col sub-matrices to the right and row sub-matrices down
// from the upper-left corner of A
__device__ Matrix GetSubMatrix(Matrix A, int row, int col) {
Matrix Asub;
Asub.width = BLOCK_SIZE;
Asub.height = BLOCK_SIZE;
Asub.stride = A.stride;
Asub.elements = &A.elements[A.stride * BLOCK_SIZE * row + BLOCK_SIZE * col];
return Asub;
}
// Matrix multiplication kernel called by MatMul()
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C) {
// Block row and column
int blockRow = blockIdx.y;
int blockCol = blockIdx.x;
int rowTest = blockIdx.y * blockDim.y + threadIdx.y;
int colTest = blockIdx.x * blockDim.x + threadIdx.x;
if (rowTest>A.height || colTest>B.width)
return;
// Each thread block computes one sub-matrix Csub of C
Matrix Csub = GetSubMatrix(C, blockRow, blockCol);
// Each thread computes one element of Csub
// by accumulating results into Cvalue
float Cvalue = 0.0;
// Thread row and column within Csub
int row = threadIdx.y;
int col = threadIdx.x;
// Loop over all the sub-matrices of A and B that are
// required to compute Csub
// Multiply each pair of sub-matrices together
// and accumulate the results
for (int m = 0; m < (BLOCK_SIZE + A.width - 1)/BLOCK_SIZE; ++m) {
// Get sub-matrix Asub of A
Matrix Asub = GetSubMatrix(A, blockRow, m);
// Get sub-matrix Bsub of B
Matrix Bsub = GetSubMatrix(B, m, blockCol);
// Shared memory used to store Asub and Bsub respectively
__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
// Load Asub and Bsub from device memory to shared memory
// Each thread loads one element of each sub-matrix
As[row][col] = GetElement(Asub, row, col);
Bs[row][col] = GetElement(Bsub, row, col);
// Synchronize to make sure the sub-matrices are loaded
// before starting the computation
__syncthreads();
// Multiply Asub and Bsub together
for (int e = 0; e < BLOCK_SIZE; ++e)
{
Cvalue += As[row][e] * Bs[e][col];
}
// Synchronize to make sure that the preceding
// computation is done before loading two new
// sub-matrices of A and B in the next iteration
__syncthreads();
}
// Write Csub to device memory
// Each thread writes one element
SetElement(Csub, row, col, Cvalue);
}
MatMulKernel に、現在のスレッドが C 言語内の存在しない場所を処理しようとしているかどうかをチェックする機能を追加しました。これはうまくいかないようです。結果は変わりますが、その変化は、後の(より高い x または y 値の)エントリがより影響を受けるように見えるということ以外のパターンを持っていないようです(そして、より多くの非整数の結果を得ます)。また、与えられた dimGrid の計算方法と MatMulKernel の m のループ条件も変更しました(以前は幅または高さをブロックサイズで割っただけでしたが、これは間違っているようでした)。
このガイドで見つけた解決ガイドでも、単純なインデックスチェックで済むはずのようなので、何かすごく根本的なことを見落としているような気がするのです。
解決方法は?
行列の寸法がタイルの寸法の倍数でない場合、いくつかのタイルが行列を部分的にしかカバーしないことが起こり得ます。完全に重なっていないタイルの外側にあるタイル要素は、適切にゼロ化される必要があります。したがって,コードを任意の大きさの行列に拡張するのは簡単ですが,単純なインデックスチェックでは意味がありません.以下、私のバージョンのタイル型行列-行列乗算カーネルを任意の大きさの行列でコピー&ペーストしています。
__global__ void MatMul(float* A, float* B, float* C, int ARows, int ACols, int BRows,
int BCols, int CRows, int CCols)
{
float CValue = 0;
int Row = blockIdx.y*TILE_DIM + threadIdx.y;
int Col = blockIdx.x*TILE_DIM + threadIdx.x;
__shared__ float As[TILE_DIM][TILE_DIM];
__shared__ float Bs[TILE_DIM][TILE_DIM];
for (int k = 0; k < (TILE_DIM + ACols - 1)/TILE_DIM; k++) {
if (k*TILE_DIM + threadIdx.x < ACols && Row < ARows)
As[threadIdx.y][threadIdx.x] = A[Row*ACols + k*TILE_DIM + threadIdx.x];
else
As[threadIdx.y][threadIdx.x] = 0.0;
if (k*TILE_DIM + threadIdx.y < BRows && Col < BCols)
Bs[threadIdx.y][threadIdx.x] = B[(k*TILE_DIM + threadIdx.y)*BCols + Col];
else
Bs[threadIdx.y][threadIdx.x] = 0.0;
__syncthreads();
for (int n = 0; n < TILE_DIM; ++n)
CValue += As[threadIdx.y][n] * Bs[n][threadIdx.x];
__syncthreads();
}
if (Row < CRows && Col < CCols)
C[((blockIdx.y * blockDim.y + threadIdx.y)*CCols) +
(blockIdx.x * blockDim.x)+ threadIdx.x] = CValue;
}
関連
-
[解決済み】エラー:cの入力の最後に期待される宣言またはステートメント
-
[解決済み】Valgrind が "Invalid write of size 8" で文句を言う。
-
[解決済み】 strcpyとstrdupの比較
-
[解決済み] clang: error: linker command failed with exit code 1が表示されるのはなぜですか?
-
[解決済み】C言語で浮動小数点例外(コアダンプ)発生
-
[解決済み】C言語で多重定義を防ぐには?
-
[解決済み】インクリメントオペランドとして lvalue が必要です。
-
[解決済み】なぜか。"エラー: 配列型を持つ式への代入"
-
[解決済み】Makefile:1: ***セパレータがありません。停止します。
-
[解決済み] '{'トークンの前に期待される式
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】組み込み関数「malloc」の暗黙の宣言の非互換性
-
[解決済み】 error: too few arguments to function `printDay' (C言語)
-
[解決済み】 strcpyとstrdupの比較
-
[解決済み] clang: error: linker command failed with exit code 1が表示されるのはなぜですか?
-
[解決済み】エラー:イニシャライザー要素がロード時に計算可能でない
-
[解決済み】メモリー・クロバリング・エラー
-
[解決済み】fgetsによるセグメンテーションフォールト(コアダンプ) - と思う。
-
[解決済み】makefile:4。*** missing separator. 停止する
-
[解決済み】Errno: 11, Resource Temporarily Unavailable(リソースが一時的に利用できない
-
[解決済み】whileループの時間複雑性(Big O)はどうやったらわかるの?