0

複数の GPU で cuda を使用してコーディングする方法を学習しています。私のデバイスの計算能力は 4.0 なので、1 つのホスト スレッドを使用して複数の GPU の cuda をコーディングできることを理解しています。まず、SDK のサンプルの 1 つ「simpleMultiGPU.cpp」を参照し、1 つまたは 2 つの GPU を使用して実行しました。どちらの場合もパフォーマンスは非常に似ていることがわかりました。つまり、2 つの GPU でコードを実行すると、1 つの GPU で実行するのと同じくらい遅い/速いということです。最適化されたコードではないことは理解していますが、これは実際に私の目の前にある例であり、機能していません。2 つのデバイスのコードはシリアルで実行されていると思います。

この SDK の例を実行しているときに、誰かが同じ問題を経験しましたか?

この SDK の例に基づいて単純なベクトル加算コードを作成しましたが、同様の方法で実行されます (予想どおり)。非同期の cuda 呼び出しを使用しており、固定されたホスト メモリも使用しています。この動作の背後にある理由を理解しようとしています。

どんな洞察も高く評価されます。

メインコードのコピーは次のとおりです。

typedef struct {
float* vec;
int N;
} vector;

extern "C" {

//Define kernel for vector addition
__global__ void vecadd_kernel(float *avec, int N, float* bvec, float *cvec){
int tId=blockIdx.x*blockDim.x+threadIdx.x;

if(tId < N)
    cvec[tId]=avec[tId]+bvec[tId];


}

void launch_addvec_kernel(float *avec, int N, float* bvec, float *cvec, int THREAD_N, int BLOCK_N, cudaStream_t &s){

vecadd_kernel<<< BLOCK_N, THREAD_N, 0, s >>> (avec,N,bvec,cvec);
        getLastCudaError("reduceKernel() execution failed.\n");

}


}

int main(){

clock_t lapse;
float cpu_time;
lapse=clock();
vector avec, bvec, cvec, cvec_gpu;

int N=256*256*256;
int threads=256;

avec.N=N; 
bvec.N=avec.N; 
cvec.N=avec.N; 

avec.vec=(float*)malloc(sizeof(float)*avec.N);
bvec.vec=(float*)malloc(sizeof(float)*bvec.N);
cvec.vec=(float*)malloc(sizeof(float)*cvec.N);
cvec_gpu.vec=(float*)malloc(sizeof(float)*avec.N);

for(int i=0;i<avec.N;++i){
    avec.vec[i]=i;
    bvec.vec[i]=i;
}


//Normal CPU addition
#pragma unroll
for(int i=0;i<avec.N;++i){
    cvec.vec[i]=avec.vec[i]+bvec.vec[i];
}

cpu_time=clock()-lapse;

printf("CPU execution time = %f seconds \n",cpu_time/CLOCKS_PER_SEC);

//-------------------- Multi-GPU code -------------------------------
//-------------------- Multi-GPU code -------------------------------
//-------------------- Multi-GPU code -------------------------------

//Get number of CUDA enabled devices
lapse=clock();
int deviceCount;
cudaGetDeviceCount(&deviceCount);
//deviceCount=1;

vector apartvecs[deviceCount], bpartvecs[deviceCount], cpartvecs[deviceCount];
vector apartvecs_gpu[deviceCount], bpartvecs_gpu[deviceCount],  cpartvecs_gpu[deviceCount];

int i,j;

    //Subdividing input data across GPUs
    //Get data sizes for each GPU
for (i=0; i<deviceCount; ++i)
    apartvecs[i].N = N/deviceCount;

    //Take into account "odd" data sizes
for (i=0; i<N%deviceCount; ++i)
    ++apartvecs[i].N; 


int offset[deviceCount];

offset[0]=0;
offset[1]=apartvecs[0].N;

cudaStream_t stream[deviceCount];

    //Create streams for issuing GPU command asynchronously and allocate memory (GPU and System page-locked)
for (i=0; i<deviceCount; ++i){
        checkCudaErrors( cudaSetDevice(i) );
        checkCudaErrors( cudaStreamCreate(&stream[i]) );

    cpartvecs[i].vec=(float*)malloc(sizeof(float)*apartvecs[i].N);

    memset(cpartvecs[i].vec,'\0',sizeof(float)*apartvecs[i].N);

    //Allocate device memory
        checkCudaErrors( cudaMalloc((void**)&apartvecs_gpu[i].vec, apartvecs[i].N * sizeof(float)) );
        checkCudaErrors( cudaMalloc((void**)&bpartvecs_gpu[i].vec, apartvecs[i].N * sizeof(float)) );
        checkCudaErrors( cudaMalloc((void**)&cpartvecs_gpu[i].vec, apartvecs[i].N * sizeof(float)) );

    //Allocate pinned memory on host
        checkCudaErrors( cudaMallocHost((void**)&apartvecs[i].vec, apartvecs[i].N * sizeof(float)));
        checkCudaErrors( cudaMallocHost((void**)&bpartvecs[i].vec, apartvecs[i].N * sizeof(float)));

    for (j=0;j<apartvecs[i].N;++j){
        int j1=j+offset[i]; 
        apartvecs[i].vec[j]=avec.vec[j1];
        bpartvecs[i].vec[j]=bvec.vec[j1];
        //printf("%d \t %d \t %d \t %d \t %f\n",i,j,offset[i],j1,apartvecs[i].vec[j]);
    }
}

    //Copy data to GPU, launch the kernel and copy data back. All asynchronously
for (i=0; i<deviceCount; ++i){

        //Set device
        checkCudaErrors( cudaSetDevice(i) );

        //Copy input data from CPU
        checkCudaErrors( cudaMemcpyAsync(apartvecs_gpu[i].vec, apartvecs[i].vec, apartvecs[i].N * sizeof(float), cudaMemcpyHostToDevice, stream[i]) );
        checkCudaErrors( cudaMemcpyAsync(bpartvecs_gpu[i].vec, bpartvecs[i].vec, apartvecs[i].N * sizeof(float), cudaMemcpyHostToDevice, stream[i]) );

    int numblocks = N/threads;

//  printf("before kernel %d \n",apartvecs[i].N);
    launch_addvec_kernel(apartvecs_gpu[i].vec,apartvecs[i].N,bpartvecs_gpu[i].vec,cpartvecs_gpu[i].vec,threads,numblocks,stream[i]);

        //Read back GPU results

        checkCudaErrors( cudaMemcpyAsync(cpartvecs[i].vec, cpartvecs_gpu[i].vec, apartvecs[i].N * sizeof(float), cudaMemcpyDeviceToHost, stream[i]) );

    //printf("here 5\n");

}

    //Process GPU results
    for(i = 0; i < deviceCount; i++){

        //Set device
        checkCudaErrors( cudaSetDevice(i) );

        //Wait for all operations to finish
        cudaStreamSynchronize(stream[i]);

// cudaDeviceSynchronize();

    for(int j=0; j<apartvecs[i].N; ++j){
        int j1=j+offset[i]; 
        cvec_gpu.vec[j1]=cpartvecs[i].vec[j];
        //printf("%d \t %d \t %d \t %d \t %f\n",i,j,offset[i],j1,cvec_gpu.vec[j1]);
    }

        //Shut down this GPU
        checkCudaErrors( cudaFreeHost(apartvecs[i].vec) );
        checkCudaErrors( cudaFreeHost(bpartvecs[i].vec) );
        checkCudaErrors( cudaFree(apartvecs_gpu[i].vec) );
        checkCudaErrors( cudaFree(bpartvecs_gpu[i].vec) );
        checkCudaErrors( cudaFree(cpartvecs_gpu[i].vec) );
        checkCudaErrors( cudaStreamDestroy(stream[i]) );

}

free(avec.vec);
free(bvec.vec);
free(cvec.vec);
free(cvec_gpu.vec);
cpu_time=clock()-lapse;

printf("GPU execution time = %f seconds \n",cpu_time/CLOCKS_PER_SEC);

}

4

0 に答える 0