2016-11-29 46 views
0

スカラー関数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 

答えて

1

CUBLASデバイスの呼び出しは非同期です。

これは、cublasコールが終了する前に呼び出し側のスレッドに制御を戻すことを意味します。

呼び出し元のスレッドが結果を直接処理できるようにする場合は(計算する場合はresを計算するために)、計算を開始する前に結果を待つように強制する必要があります。

親カーネルが終了する前にデバイスアクティビティ(cublasデバイスの動的並列処理を含む)が暗黙的に同期されているため、これはホスト側の計算では表示されません。だから、

あなたは、デバイスCUBLASはこのように、呼び出しの後に同期を追加追加した場合:

cublasStatus_t status=cublasSgetrfBatched(*hdl, *n, a, *n, p, info, batch); 
cudaDeviceSynchronize(); // add this line 

私はあなたが期待どおりには、デバイスの計算とホスト計算との間の一致を見ると思います。

+0

多くのありがとう、ロバート。あなたの提案は機能し、期待される結果を生み出すことができます。最初にcublasSgetrfBatchedを使用してLU分解を取得し、次にcublasSgetriBatchedを使用してLU出力の逆関数を取得する別のデバイスルーチンがあります。それらの間で、私はcudaDeviceSynchronize()を使用する必要がありますか?小行列(3×3)の場合、結果は同じように見えます。 – Jack

+0

もう1つの質問は、デバイスルーチンでmallocによって作成されたメモリを解放することに関連しています。メモリを解放するためにfree()を使用すると、コードが-lcublas_device(コンパイルされていない状態)でコンパイルされると、cuda-memcheckはエラーを生成します。あなたはこれを回避するためのアイディアを持っていますか?私が記憶を解放しなければ、どんな結果になるでしょうか?ありがとう。 – Jack

+0

最初の質問に関しては、cuda [ストリームセマンティクス](http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#streams)もデバイス側の操作に適用されます。デフォルトのストリーム(スレッド単位で起動する)を指定しないデバイス側の起動では、デバイスのcublas操作Aと同じスレッドによって発行されたデバイスのcublas操作Bが、同じストリームに発行される必要があります。 Bは、Aが終了するまで開始してはなりません。 2番目の質問に関して、私はあなたが 'free()'操作をしている場所を正確に知る必要があります。 –

関連する問題