C++ 改进CUDA中的异步执行
我目前正在编写一个程序,使用CUDAAPI在GPU上执行大型模拟。为了提高性能,我尝试同时运行内核,然后将结果异步复制到主机内存中。代码大致如下所示:C++ 改进CUDA中的异步执行,c++,cuda,gpu,gpgpu,C++,Cuda,Gpu,Gpgpu,我目前正在编写一个程序,使用CUDAAPI在GPU上执行大型模拟。为了提高性能,我尝试同时运行内核,然后将结果异步复制到主机内存中。代码大致如下所示: #define NSTREAMS 8 #define BLOCKDIMX 16 #define BLOCKDIMY 16 void domainUpdate(float* domain_cpu, // pointer to domain on host float* domain_gpu,
#define NSTREAMS 8
#define BLOCKDIMX 16
#define BLOCKDIMY 16
void domainUpdate(float* domain_cpu, // pointer to domain on host
float* domain_gpu, // pointer to domain on device
const unsigned int dimX,
const unsigned int dimY,
const unsigned int dimZ)
{
dim3 blocks((dimX + BLOCKDIMX - 1) / BLOCKDIMX, (dimY + BLOCKDIMY - 1) / BLOCKDIMY);
dim3 threads(BLOCKDIMX, BLOCKDIMY);
for (unsigned int ii = 0; ii < NSTREAMS; ++ii) {
updateDomain3D<<<blocks,threads, 0, streams[ii]>>>(domain_gpu,
dimX, 0, dimX - 1, // dimX, minX, maxX
dimY, 0, dimY - 1, // dimY, minY, maxY
dimZ, dimZ * ii / NSTREAMS, dimZ * (ii + 1) / NSTREAMS - 1); // dimZ, minZ, maxZ
unsigned int offset = dimX * dimY * dimZ * ii / NSTREAMS;
cudaMemcpyAsync(domain_cpu + offset ,
domain_gpu+ offset ,
sizeof(float) * dimX * dimY * dimZ / NSTREAMS,
cudaMemcpyDeviceToHost, streams[ii]);
}
cudaDeviceSynchronize();
}
#定义n流8
#定义块DIMX 16
#定义块DIMY 16
void domainUpdate(float*domain\u cpu,//指向主机上的域的指针
float*domain\gpu,//指向设备上的域的指针
常量无符号整数dimX,
常量无符号整数dimY,
常量无符号整数(dimZ)
{
dim3块((dimX+块dimX-1)/BLOCKDIMX,(dimY+块dimY-1)/BLOCKDIMY);
dim3螺纹(块状DIMX、块状DIMY);
对于(无符号整数ii=0;ii
总之,它只是一个简单的for循环,在所有流上循环(在本例中为8),并划分工作。这实际上是一个更快的交易(高达30%的性能增益),虽然可能比我希望的要少。我分析了Nvidia的Compute Visual Profiler中的一个典型周期,执行过程如下所示:
#define NSTREAMS 8
#define BLOCKDIMX 16
#define BLOCKDIMY 16
void domainUpdate(float* domain_cpu, // pointer to domain on host
float* domain_gpu, // pointer to domain on device
const unsigned int dimX,
const unsigned int dimY,
const unsigned int dimZ)
{
dim3 blocks((dimX + BLOCKDIMX - 1) / BLOCKDIMX, (dimY + BLOCKDIMY - 1) / BLOCKDIMY);
dim3 threads(BLOCKDIMX, BLOCKDIMY);
for (unsigned int ii = 0; ii < NSTREAMS; ++ii) {
updateDomain3D<<<blocks,threads, 0, streams[ii]>>>(domain_gpu,
dimX, 0, dimX - 1, // dimX, minX, maxX
dimY, 0, dimY - 1, // dimY, minY, maxY
dimZ, dimZ * ii / NSTREAMS, dimZ * (ii + 1) / NSTREAMS - 1); // dimZ, minZ, maxZ
unsigned int offset = dimX * dimY * dimZ * ii / NSTREAMS;
cudaMemcpyAsync(domain_cpu + offset ,
domain_gpu+ offset ,
sizeof(float) * dimX * dimY * dimZ / NSTREAMS,
cudaMemcpyDeviceToHost, streams[ii]);
}
cudaDeviceSynchronize();
}
从图中可以看出,内核确实重叠,尽管在同一时间运行的内核不会超过两个。我对不同数量的流和不同大小的模拟域尝试了相同的方法,但情况总是如此
所以我的问题是:有没有办法鼓励/强制GPU调度程序同时运行两件以上的事情?或者这是一个限制,依赖于无法在代码中表示的GPU设备
我的系统规格是:64位Windows 7和GeForce GTX 670图形卡(这是开普勒体系结构,计算能力3.0)。只有当GPU还有资源运行第二个内核时,内核才会重叠。一旦GPU完全加载,并行运行更多内核不会带来任何好处,因此驱动程序不会这样做。但即使是非常小的内核,如许多块,也不会同时运行超过2个内核。所以GPU的物理尺寸不可能是全部,是吗?是的,它可以。什么是“小内核”?有几个街区?每个块有多少线程?他们使用共享内存吗?登记?除非您分析了内核的资源利用率,否则您不知道可以运行多少个内核。Windows(当GPU处于WDDM模式时)也可以通过批处理GPU活动来干扰并发性。GPU并不局限于同时运行两件事情。这是一个好的观点,我没有完全考虑所有共享内存和寄存器的要求,我不太明白这会影响性能到什么程度。例如,我尝试的一个“小”内核是带有16x16线程的8x8块。其中,我认为GPU在理论上应该适合更多的负载。它每个线程使用33个寄存器,每个块大约2 kB的共享内存。这太多了吗?使用占用率计算器来计算一个多处理器上可以并发运行的块数。对于内核,它是6个块/SMX,因此GTX670的7个SMX可以同时运行42个块。对于64个块的网格,第一波并发块将在第二波中留下22个块进行处理,如果第二次启动同一内核(但不足以进行第三次启动),将为另外20个块留出空间。完美地解释了你在探查器中的发现。感谢你的清晰解释,现在这更有意义了!