したがって、これは自分で実装する必要があるかもしれません。ライブラリがそれをやってくれないからです。(おそらく、BLAS レベル 3 ルーチン (確かに行列要素の 2 乗) の観点から、それを実装する方法がいくつかありますが、それには費用がかかり、さもなければ不必要な行列とベクトルの乗算が必要になります。 d 平方根演算を行います)。その理由は、これらの操作が実際には線形代数の手順ではないためです。各行列要素の平方根を取ることは、基本的な線形代数操作に実際には対応していません。
幸いなことに、これらの要素単位の操作は CUDA で非常に簡単に実装できます。繰り返しになりますが、最高のパフォーマンスを得るために使用できるチューニング オプションはたくさんありますが、かなり簡単に始めることができます。
行列の加算演算と同様に、ここでは NxM 行列を (N*M) の長さのベクトルとして扱います。これらの要素ごとの操作では、行列の構造は重要ではありません。したがって、行列の最初の要素へのポインターを渡し、それを N*M の数値の単一のリストとして扱います。float
(先ほど話していたように、ここでは sSGEMM
を使用していると仮定しますSAXPY
。)
操作を実装する CUDA コードの実際のビットであるカーネルは、非常に単純です。今のところ、各スレッドは 1 つの配列要素の 2 乗 (または平方根) を計算します。(これがパフォーマンスにとって最適かどうかは、テストできるものです)。したがって、カーネルは次のようになります。B_ij = (A_ij)^2; のようなことをしていると思います。A_ij = (A_ij)^2 のようにその場で演算を実行したい場合は、それも実行できます。
__global__ void squareElements(float *a, float *b, int N) {
/* which element does this compute? */
int tid = blockDim.x * blockIdx.x + threadIdx.x;
/* if valid, squre the array element */
if (tid < N)
b[tid] = (a[tid]*a[tid]);
}
__global__ void sqrtElements(float *a, float *b, int N) {
/* which element does this compute? */
int tid = blockDim.x * blockIdx.x + threadIdx.x;
/* if valid, sqrt the array element */
if (tid < N)
b[tid] = sqrt(a[tid]); /* or sqrtf() */
}
エラーがわずかに増加しても問題ない場合は、最大エラーが 3 ulp (最後の桁の単位) の 'sqrtf()' 関数の方が大幅に高速であることに注意してください。
これらのカーネルをどのように呼び出すかは、実行する順序によって異なります。これらの行列で既にいくつかの CUBLAS 呼び出しを行っている場合は、既に GPU メモリにある配列でそれらを使用する必要があります。