C++ 改进CUDA中的异步执行

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,

我目前正在编写一个程序,使用CUDAAPI在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个块留出空间。完美地解释了你在探查器中的发现。感谢你的清晰解释,现在这更有意义了!