多线程 同时访问一个gpu的问题

最近导师弄个个kelper的架构的显卡给我们弄 主要想尝试一下多cpu线程同时访问gpu 但是试着用openmp来开线程同时访问gpu 通过gpu observer 来观察 gpu的利用率一直没有变还是跟单线程执行相同的函数利用是一样的 执行的时间也翻倍了 也就是还是 串行来做的 想请教大大们一下有没有这方面的例程 可以借鉴参考一下啊

LZ您好:

请您提供您的具体实现的写法,以便分析。

另外,请不要使用默认stream,默认stream中隐含的同步机制会破坏多stream的并行性。

祝您好运~

#include<stdio.h>
#include<cuda_runtime.h>
#include<omp.h>
#include<time.h>
#include<opencv2/core/core.hpp>
#include<iostream>

__global__ void
vectorAdd(const float *A,const float *B, float *C,int numElements)
{
   int i = blockDim.x * blockIdx.x + threadIdx.x;
   if (i < numElements)
   {
   C[i] = A[i] * B[i];
   }
}

int main(void)
{
   
   cudaError_t err = cudaSuccess;
   int numElements = 5000000;
   size_t size = numElements * sizeof(float);
   printf("[Vector addition of %d elements]\n", numElements);

   float *h_a = (float *)malloc(size);
   float *h_b = (float *)malloc(size);
   float *h_c = (float *)malloc(size);

   for(int i = 0; i < numElements;++i)
   {
   h_a[i] = rand()/(float)RAND_MAX;
   h_b[i] = rand()/(float)RAND_MAX;
   }

   float *d_a = NULL;
   err = cudaMalloc((void**)&d_a,size);

   float *d_b = NULL;
   err = cudaMalloc((void **)&d_b,size);
   
   float *d_c = NULL;
   err = cudaMalloc((void**)&d_c,size);

   printf("Copy input data from the host memory to the CUDA device\n");
   err = cudaMemcpy(d_a,h_a,size,cudaMemcpyHostToDevice);
   err = cudaMemcpy(d_b,h_b,size,cudaMemcpyHostToDevice);
/////////////////////////////////////////////////////////
   float *h_a1 = (float *)malloc(size);
   float *h_b1 = (float *)malloc(size);
   float *h_c1 = (float *)malloc(size);

   for(int i = 0; i < numElements;++i)
   {
   h_a1[i] = rand()/(float)RAND_MAX;
   h_b1[i] = rand()/(float)RAND_MAX;
   }

   float *d_a1 = NULL;
   err = cudaMalloc((void**)&d_a1,size);

   float *d_b1 = NULL;
   err = cudaMalloc((void **)&d_b1,size);
   
   float *d_c1 = NULL;
   err = cudaMalloc((void**)&d_c1,size);

   printf("Copy input data from the host memory to the CUDA device\n");

   err = cudaMemcpy(d_a1,h_a1,size,cudaMemcpyHostToDevice);
   err = cudaMemcpy(d_b1,h_b1,size,cudaMemcpyHostToDevice);
/////////////////////////////////////////////////////////
   int threadsPerBlock = 256;
   int blocksPerGrid = (numElements + threadsPerBlock - 1)/threadsPerBlock;
   //long t1 = clock();
   int64 t = cv::getTickCount();
#pragma omp parallel sections
   {
#pragma omp section
   {

   printf("section1 threadid = %d\n",omp_get_thread_num());
   printf("cuda kernel launch with %d blocks of %d threads\n",blocksPerGrid,threadsPerBlock);

   //vectorAdd<<blocksPerGrid,threadsPerBlock>>(d_a,d_b,d_c,numElements);
   vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c, numElements);
   }
#pragma omp section
   {
   printf("section2 threadid = %d\n",omp_get_thread_num());
   printf("cuda kernel launch with %d blocks of %d threads\n",blocksPerGrid,threadsPerBlock);

   //vectorAdd<<blocksPerGrid,threadsPerBlock>>(d_a,d_b,d_c,numElements);
   vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_a1, d_b1, d_c1, numElements);
   }
   }
   //long t2 = clock();
   //printf("cost time %ld\n",t2-t1);
   std::cout<<((cv::getTickCount() - t) / cv::getTickFrequency())*1000 << " msec"<<std::endl;

   err = cudaGetLastError();

   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to launch vectorAdd kernel (error code %s)!\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }

   // Copy the device result vector in device memory to the host result vector
   // in host memory.
   printf("Copy output data from the CUDA device to the host memory\n");
   

   err =cudaMemcpy(h_c,d_c,size,cudaMemcpyDeviceToHost);
   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to copy vector C from device to host (error code %s)!\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }

   // Verify that the result vector is correct
   for (int i = 0; i < numElements; ++i)
   {
   if (fabs(h_a[i] *h_b[i] - h_c[i]) > 1e-5)
   {
   fprintf(stderr, "Result verification failed at element %d!\n", i);
   exit(EXIT_FAILURE);
   }
   }
   printf("Test PASSED\n");
  err = cudaFree(d_a);

   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to free device vector A (error code %s)!\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }
   err = cudaFree(d_b);

   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to free device vector B (error code %s)!\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }
   err = cudaFree(d_c);

   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to free device vector C (error code %s)!\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }

   // Free host memory
   free(h_a);
   free(h_b);
   free(h_c);

   // Reset the device and exit
   err = cudaDeviceReset();

   if (err != cudaSuccess)
   {
   fprintf(stderr, "Failed to deinitialize the device! error=%s\n", cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }

   printf("Done\n");
   return 0;
}

你看我这种思路 可以实现 多个线程同时访问gpu么 直接把sdk中向量相加的例子 改的

LZ您好:

不能为您指出OPENMP的相关用法是否正确的建议。

以及,多个host端线程向同一个GPU发布kernel等是可以的。
但是一般这样是为了多个逻辑依赖链之间方便隔离才这样的,以及需要使用多个stream。

您的写法所有的kernel都在同一个default stream中,用的也都是同步的copy函数,这无法实现不同的stream之间相互掩盖传输的作用,也无法实现多个小线程规模的kernel同时执行的作用,而仅仅是大家都往同一个GPU上发布任务而已。

大致如此,祝您好运~

太感谢您了 我大概明白要在哪方面 下功夫

不客气的,欢迎您常来~

#include<cuda.h>
#include<stdio.h>
#include<stdlib.h>
#include<cv.h>
#include<highgui.h>
#include<cxcore.h>
#include<math_functions.h>
#include<cuda_runtime.h>
#include<time.h>

#include<iostream>


const char *filename[16] = {"test1.bmp","test2.bmp","test3.bmp","test4.bmp","test5.bmp","test6.bmp","test7.bmp","test8.bmp","test9.bmp","test10.bmp","test11.bmp","test12.bmp","test13.bmp","test14.bmp","test15.bmp","test16.bmp"};
const int nstreams = 16;
const int interation = 20;
void checkCUDAError(const char* msg);

__global__ void edge_gpu(unsigned char* buff , unsigned char* buffer_out , int w , int h)
{
   int x = blockIdx.x * blockDim.x +threadIdx.x ;
   int y = blockIdx.y * blockDim.y +threadIdx.y; 
   int width = w , height = h ;

   if((x>=0 && x < width) && (y>=0 && y<height))
   {
   int hx = -buff[width*(y-1) + (x-1)] + buff[width*(y-1)+(x+1)]
   -2*buff[width*(y)+(x-1)] + 2*buff[width*(y)+(x+1)]
   -buff[width*(y+1)+(x-1)] + buff[width*(y+1)+(x+1)];

   int vx = buff[width*(y-1)+(x-1)] +2*buff[width*(y-1)+(x+1)] +buff[width*(y-1)+(x+1)]
   -buff[width*(y+1)+(x-1)] - 2*buff[width*(y+1)+(x)] - buff[width*(y+1)+(x+1)];


   int val = (int)sqrt((float)(hx) * (float)(hx) + (float)(vx) * (float)(vx));                                        

   buffer_out[y * width + x] = (unsigned char) val;                                                        
   }
}


void checkCUDAError(const char* msg) 
{
   cudaError_t err = cudaGetLastError();
   if (cudaSuccess != err) 
   {
   fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString(err));
   exit(EXIT_FAILURE);
   }
}



int main(int argc , char** argv)
{
   int nWidth = 2448;
   int nHeight = 2048;
   IplImage *temp[nstreams];
   for(int i = 0;i < nstreams;i++)
   temp[i] = cvLoadImage(filename[i],0);

   int buffer_size = nWidth * nHeight;

   unsigned char *buffer[nstreams];
   unsigned char* buf[nstreams] ;
   unsigned char *buffer_dev[nstreams];
   unsigned char *buffer_out[nstreams];

   for(int i = 0;i < nstreams;i++)
   buffer[i] = (unsigned char*)(temp[i]->imageData);


   for(int i = 0;i < nstreams;i++){
   //buf[i] = (unsigned char*) malloc(buffer_size);
   cudaMallocHost((void**)&buf[i],buffer_size);
   checkCUDAError("Memory Allocation");
   }
   for(int i = 0;i < nstreams;i++){
   cudaMalloc((void**)&buffer_out[i],buffer_size);
   checkCUDAError("Memory Allocation");
   }

   for(int i = 0;i < nstreams;i++){
   cudaMalloc((void**)&buffer_dev[i],buffer_size);
   checkCUDAError("Memory Allocation");
   }




   //==========  create a stream  ==========

   cudaStream_t *streams = (cudaStream_t*)malloc(nstreams * sizeof(cudaStream_t));
   for(int i = 0;i < nstreams;i++)
   cudaStreamCreate(&(streams[i]));
   //=========================================        

   for(int i = 0;i< nstreams;i++){
   cudaMemcpy(buffer_dev[i] , buffer[i] , buffer_size , cudaMemcpyHostToDevice);
   std::cout<<i<<std::endl;
   checkCUDAError("Memory Copy From Host To Device");
   }
   dim3 threadsPerBlock(8,8);
   dim3 numBlocks((nWidth)/8,(nHeight)/8);

   for(int i = 0;i < nstreams;i++){
   edge_gpu<<< numBlocks , threadsPerBlock , 0 , streams[i] >>>(buffer_dev[i] , buffer_out[i], nWidth , nHeight);
   checkCUDAError("Kernel");
   }

   //for (int i = 0; i < nstreams; i ++)
   //        cudaStreamSynchronize(streams[i]);

   for(int i = 0;i < nstreams;i++){
   cudaMemcpy(buf[i] , buffer_out[i] , buffer_size  , cudaMemcpyDeviceToHost);
   checkCUDAError("Memory Copy From Device To Host");
   }


   //==========Check the result==============
   //memcpy(buffer[1],buf[1],buffer_size);//copying memory to show image
   //cvNamedWindow("0");
   //cvShowImage("0",temp[1]);
   //cvWaitKey(0);
   for(int i = 0;i < nstreams;i++)
   cudaFree(&(buffer_dev[i]));

   for(int i = 0;i < nstreams;i++)
   cudaFree(&(buffer_out[i]));

   for(int i = 0;i < nstreams;i++)
   cudaStreamDestroy(streams[i]);        

   //for(int i = 0;i < nstreams;i++)
   //free(&(buf[i]));
   for(int i = 0;i < nstreams;i++)
   cudaFreeHost(buf[i]);

   for(int i = 0;i < nstreams;i++)
   cvReleaseImage(&(temp[i]));

   free(streams);

   printf("\n\nDONE...!!...Copy Successful...!!\n\n");

   return 1;
}

楼主您好,

有问题请直接询问。

不要贴个图片就走人,让人猜测您心里的问题是什么。

谢谢。

哥们 我按照您的思路写了一段图像处理的 小代码 但是在nsight中调试 观察到stream 并不是并行的 我用的计算能力为3.5的显卡 而且sdk中hyperq的小例子调试出来也是并行的 您能指点一下 我这段代码哪儿有问题么 谢谢了 代码具体在上一楼

您好不好意思 我编辑的 有问题 所以贴在下一楼了 我想问 我用的是kepler架构的显卡 我这段代码 stream 为啥无法并行

楼主您这里引入了隐式的同步(cudaMemcpy会导致自动的同步的),所以您看不到并行非常正常。

您应当修正为使用异步版本的cudaMemcpyAsync + pinned memory, 才有可能观察到传输和复制并行。

以及,后者就算您正确的写了,您可能需要Tesla卡才能看到效果。特别从CUDA 5.0的配套驱动(及以后版本)开始,对于geforce卡的多流并行效果减弱了,更加不容易看到。

请您知悉正确的做法(第二段的cudaMemcpyAsync), 以及可能的结果。

感谢来访。

好的 您好哥们 我刚刚开始弄 我正在看异步方面的知识 我用的是 gtx780 也可能无法看到效果么

关于是否GTX780上能看到效果,这个不一定的。
(没说普通的Geforce卡上一定看不到效果,只是说不容易看到)

空说无益,请您尝试我之前的建议(cudaMemcpyAsync + pinned memory),并反馈结果。

好的 我做一下 在跟您讨论一下结果 thanks :slight_smile: 但是哥们我用sdk的example中的hyperQ的例子 在nsight中看到的 确实是并行的 感觉 那个例子 其实跟这段程序并没有大的区别啊

楼主您好,我手头没有3.5的卡,无法为您评估此Hyper-Q的效果。

我建议您自己测试下,然后发到论坛,这样可以受益很多人(包括没卡的),
鼓励他们积极买卡,共同构建CUDA的绿色生态环境。

您说呢?

感谢您的莅临。

嗯 好的 谢了