多个主机线程一起启动CUDA内核

时间:2014-03-20 16:56:31

标签: cuda openmp

我遇到了一个非常奇怪的情况。这是我们的代码:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>


void initCuda(int g)
{
    cudaDeviceProp prop;
    if(cudaGetDeviceProperties(&prop, g) == cudaSuccess) printf("MP cnt: %d ,Concurrent Kernels:%d , AsyncEngineCount:%d , ThrdPerMP: %d\n",
                                                             prop.multiProcessorCount,prop.concurrentKernels,prop.asyncEngineCount,192);
    cudaSetDevice(g);
}
__global__ void cudaJob(float *mem){
  unsigned int tid=threadIdx.x+blockIdx.x*blockDim.x;
  mem[tid]=-1e5;
  while(mem[tid]<1.0e5){
    mem[tid]=mem[tid]+1e-2;
  }
}
void wrapper(int n,int b){
  float** dmem=(float**)malloc(n*(sizeof(float*)));

  cudaStream_t* stream=(cudaStream_t*)malloc(sizeof(cudaStream_t)*n);

  dim3 grid=dim3(b,1,1);
  dim3 block=dim3(192,1,1);//2496/13=192

  for(int i=0;i<n;i++) {
    cudaMalloc((void**)&dmem[i],192*b*sizeof(float));
    cudaStreamCreate(&stream[i]);
  }

  for(int i=0;i<n;i++) cudaJob<<<grid,block,0,stream[i]>>>(dmem[i]);


 for(int i=0;i<n;i++) {
    cudaStreamDestroy(stream[i]);
    cudaFree(dmem[i]);
  }


 free(stream);
 free(dmem);

}

int main(int argc,char* argv[]){

initCuda(0);
int n=atoi(argv[1]);
int nthreads=atoi(argv[2]);
int b=atoi(argv[3]);
float t1=omp_get_wtime();
#pragma omp parallel num_threads(nthreads) firstprivate(nthreads,n,b)
{
#pragma omp barrier
  float time=omp_get_wtime();
  int id=omp_get_thread_num();

  wrapper(n,b);
  time=omp_get_wtime()-time;
  printf("Num Threads: %d, Time: %f\n",id,time);
}
printf("total: %f\n",omp_get_wtime()-t1);
return 0;
}

因此,如果我们运行./main 1 8 1.这意味着它们将是8个线程,并且每个线程将启动一个内核。但是,有时实际运行时间表明内核不会同时启动:

    MP cnt: 13 ,Concurrent Kernels:1 , AsyncEngineCount:2 , ThrdPerMP: 192
Num Threads: 0, Time: 3.788108
Num Threads: 6, Time: 6.661960
Num Threads: 7, Time: 9.535245
Num Threads: 2, Time: 12.408561
Num Threads: 5, Time: 12.410481
Num Threads: 1, Time: 12.411650
Num Threads: 4, Time: 12.412888
Num Threads: 3, Time: 12.414572
total: 12.414601

经过一些调试后,我们发现问题可能是由于内存和流的清理造成的。如果我们注释掉所有cudaFree和StreamDestroy并且免费。然后运行时间将表明一切都是并发的:

MP cnt: 13 ,Concurrent Kernels:1 , AsyncEngineCount:2 , ThrdPerMP: 192
Num Threads: 7, Time: 3.805691
Num Threads: 1, Time: 3.806201
Num Threads: 3, Time: 3.806624
Num Threads: 2, Time: 3.806695
Num Threads: 6, Time: 3.807018
Num Threads: 5, Time: 3.807456
Num Threads: 0, Time: 3.807486
Num Threads: 4, Time: 3.807792
total: 3.807799  

最后我们发现,如果我们在内核启动调用后面添加一个omp屏障。然后清理不会造成任何问题:

 for(int i=0;i<n;i++) cudaJob<<<grid,block,0,stream[i]>>>(dmem[i]);

#pragma omp barrier
 for(int i=0;i<n;i++) {
    cudaStreamDestroy(stream[i]);
    cudaFree(dmem[i]);
  }

因此,我们认为当多个主机线程试图清理设备上的内存和流时,它们可能会相互竞争。但我们不确定。

是吗?任何人都可以帮助我们移除omp屏障吗?因为我们认为没有必要解决我们的问题。

1 个答案:

答案 0 :(得分:3)

是的,cudaMalloccudaFreecudaStreamCreate都是同步的,这意味着他们会倾向于序列化活动,方法是在执行之前强制执行之前发出的任何cuda调用

通常的建议是在时间关键代码之外进行所有此类分配。计算出你需要多少分配,预先分配它们,然后在主处理循环中使用(并且可能重复使用)它们,然后释放最后需要的东西。