我遇到了一个非常奇怪的情况。这是我们的代码:
#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屏障吗?因为我们认为没有必要解决我们的问题。
答案 0 :(得分:3)
是的,cudaMalloc
,cudaFree
和cudaStreamCreate
都是同步的,这意味着他们会倾向于序列化活动,方法是在执行之前强制执行之前发出的任何cuda调用
通常的建议是在时间关键代码之外进行所有此类分配。计算出你需要多少分配,预先分配它们,然后在主处理循环中使用(并且可能重复使用)它们,然后释放最后需要的东西。