スカラー関数f(x)を評価しようとしています。ここで、xはk次元のベクトルです(つまり、f:R^k-> R)。評価の間、私は多くの行列演算を実行しなければなりません:逆行列、乗法、行列の行列式と中程度の大きさの行列のトレースを見つける必要があります(大部分は30x30未満です)。今私は、GPU上の異なるスレッドを使用して、同時に多くの異なるxの関数を評価したいと思います。だから私はデバイスのAPIが必要です。cublasデバイスAPIを使用して行列の行列式を計算する
私はcublasデバイスAPI、cublasSgetrfBatchedによって行列の行列式を計算するテストを行うために次のコードを書いています。まず、行列のLU分解を見つけ、U行列のすべての対角要素の積を計算します。私は、cublasによって返された結果を使って、GPUスレッドとCPUの両方でこれを行っています。しかし、CPU上の結果が正しい間は、GPUの結果は意味をなさない。私はcuda-memcheckを使用しましたが、エラーは見つかりませんでした。誰かがこの問題についていくつかの光を当てるのを助けてくれましたか?どうもありがとう。
cat test2.cu
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
__host__ __device__ unsigned int IDX(unsigned int i,unsigned int j,unsigned int ld){return j*ld+i;}
#define PERR(call) \
if (call) {\
fprintf(stderr, "%s:%d Error [%s] on "#call"\n", __FILE__, __LINE__,\
cudaGetErrorString(cudaGetLastError()));\
exit(1);\
}
#define ERRCHECK \
if (cudaPeekAtLastError()) { \
fprintf(stderr, "%s:%d Error [%s]\n", __FILE__, __LINE__,\
cudaGetErrorString(cudaGetLastError()));\
exit(1);\
}
__device__ float
det_kernel(float *a_copy,unsigned int *n,cublasHandle_t *hdl){
int *info = (int *)malloc(sizeof(int));info[0]=0;
int batch=1;int *p = (int *)malloc(*n*sizeof(int));
float **a = (float **)malloc(sizeof(float *));
*a = a_copy;
cublasStatus_t status=cublasSgetrfBatched(*hdl, *n, a, *n, p, info, batch);
unsigned int i1;
float res=1;
for(i1=0;i1<(*n);++i1)res*=a_copy[IDX(i1,i1,*n)];
return res;
}
__global__ void runtest(float *a_i,unsigned int n){
cublasHandle_t hdl;cublasCreate_v2(&hdl);
printf("det on GPU:%f\n",det_kernel(a_i,&n,&hdl));
cublasDestroy_v2(hdl);
}
int
main(int argc, char **argv)
{
float a[] = {
1, 2, 3,
0, 4, 5,
1, 0, 0};
cudaSetDevice(1);//GTX780Ti on my machine,0 for GTX1080
unsigned int n=3,nn=n*n;
printf("a is \n");
for (int i = 0; i < n; ++i){
for (int j = 0; j < n; j++) printf("%f, ",a[IDX(i,j,n)]);
printf("\n");}
float *a_d;
PERR(cudaMalloc((void **)&a_d, nn*sizeof(float)));
PERR(cudaMemcpy(a_d, a, nn*sizeof(float), cudaMemcpyHostToDevice));
runtest<<<1, 1>>>(a_d,n);
cudaDeviceSynchronize();
ERRCHECK;
PERR(cudaMemcpy(a, a_d, nn*sizeof(float), cudaMemcpyDeviceToHost));
float res=1;
for (int i = 0; i < n; ++i)res*=a[IDX(i,i,n)];
printf("det on CPU:%f\n",res);
}
nvcc -arch=sm_35 -rdc=true -o test test2.cu -lcublas_device -lcudadevrt
./test
a is
1.000000, 0.000000, 1.000000,
2.000000, 4.000000, 0.000000,
3.000000, 5.000000, 0.000000,
det on GPU:0.000000
det on CPU:-2.000000
多くのありがとう、ロバート。あなたの提案は機能し、期待される結果を生み出すことができます。最初にcublasSgetrfBatchedを使用してLU分解を取得し、次にcublasSgetriBatchedを使用してLU出力の逆関数を取得する別のデバイスルーチンがあります。それらの間で、私はcudaDeviceSynchronize()を使用する必要がありますか?小行列(3×3)の場合、結果は同じように見えます。 – Jack
もう1つの質問は、デバイスルーチンでmallocによって作成されたメモリを解放することに関連しています。メモリを解放するためにfree()を使用すると、コードが-lcublas_device(コンパイルされていない状態)でコンパイルされると、cuda-memcheckはエラーを生成します。あなたはこれを回避するためのアイディアを持っていますか?私が記憶を解放しなければ、どんな結果になるでしょうか?ありがとう。 – Jack
最初の質問に関しては、cuda [ストリームセマンティクス](http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#streams)もデバイス側の操作に適用されます。デフォルトのストリーム(スレッド単位で起動する)を指定しないデバイス側の起動では、デバイスのcublas操作Aと同じスレッドによって発行されたデバイスのcublas操作Bが、同じストリームに発行される必要があります。 Bは、Aが終了するまで開始してはなりません。 2番目の質問に関して、私はあなたが 'free()'操作をしている場所を正確に知る必要があります。 –