Concurrency 如何使多cublasapi(如cublasDgemm)在多cudaStream中真正并发执行

Concurrency 如何使多cublasapi(如cublasDgemm)在多cudaStream中真正并发执行,concurrency,cuda,cublas,cuda-streams,Concurrency,Cuda,Cublas,Cuda Streams,我想让两个cublasapi(例如cublasDgemm)在两个cudaStreams中真正并发执行 正如我们所知,CUBLAS API是异步的,像cublasDgemm这样的3级例程不会阻塞主机,这意味着以下代码(在默认cudaStream中)将并发运行: cublasDgemm(); cublasDgemm(); 但是,当我用“NVIDIA Visual Profiler”评测程序时,它显示它们是有序运行的 然后,我尝试将它们绑定到不同的cudaStreams,伪代码是: // Creat

我想让两个cublasapi(例如cublasDgemm)在两个cudaStreams中真正并发执行

正如我们所知,CUBLAS API是异步的,像cublasDgemm这样的3级例程不会阻塞主机,这意味着以下代码(在默认cudaStream中)将并发运行:

cublasDgemm();
cublasDgemm();
但是,当我用“NVIDIA Visual Profiler”评测程序时,它显示它们是有序运行的

然后,我尝试将它们绑定到不同的cudaStreams,伪代码是:

// Create a stream for every DGEMM operation
cudaStream_t *streams = (cudaStream_t *) malloc(batch_count*sizeof(cudaStream_t));
for(i=0; i<batch_count; i++)
    cudaStreamCreate(&streams[i]);

// Set matrix coefficients
double alpha = 1.0;
double beta  = 1.0;

// Launch each DGEMM operation in own CUDA stream
for(i=0; i<batch_count; i++){
    // Set CUDA stream
    cublasSetStream(handle, streams[i]);

    // DGEMM: C = alpha*A*B + beta*C
    cublasDgemm(handle,
                CUBLAS_OP_N, CUBLAS_OP_N,
                dim, dim, dim,
                &alpha,
                d_A[i], dim,
                d_B[i], dim,
                &beta,
                d_C[i], dim);
}
//为每个DGEMM操作创建一个流
cudaStream_t*streams=(cudaStream_t*)malloc(批次计数*大小(cudaStream_t));

首先,感谢@Robert Crovella的评论

根据@Robert Crovella的帮助和我的研究,在某些特殊情况下,我们可以同时运行多个CUBLAS API(例如cublasDgemm),但大多数情况下不能

案例1:当我在K40上执行带有大dims(m=n=k=1024*8)的cublasDgemm时,探查器显示如下结果:

案例2:当我在K40上执行带有小dims(m=n=k=64)的cublasDgemm时,探查器显示如下结果:

案例3:但是当我在K40上执行cublasDgemm,dims为(m=n=k=256)时,探查器显示如下结果:


从案例1和案例2的结果可以看出,我们不仅不能在大DIM和小DIM的情况下同时运行CUBLAS API。案例1的原因是gpu资源已经用完,因此没有空间运行其他例程,而对于案例2,两个内核启动的延迟导致很难看到一致性。

A超过特定大小的gemm调用将启动具有足够块的内核,以填充GPU,从而使后续的内核启动没有并发运行的空间将倾向于执行得如此之快,以至于并发性再次难以观察到。这个问题的前提是可以使任意两个内核同时运行;这根本不是真的。观察内核并发性实际上相当困难,需要精心设计的内核启动,即使除了CUBLAS。@Robert Crovella,感谢您的com@Robert Crovella,谢谢你的评论。但我怀疑“超过特定大小的gemm调用将启动具有足够块的内核,以填充GPU,从而使后续的内核启动没有并发运行的空间。”因为当尝试使用不同维度的矩阵执行gemm时(维度足够小,这意味着gpu资源有空间运行另一个gemm),结果是一样的。当维度足够小时,内核执行时间足够短(例如几微秒)它可能包含在内核启动延迟内,因此很难或不可能看到并发。如果您不同意,请提供该情况下的探查器输出,以及内核启动中包含的块数(可从探查器中发现)。由于每个内核中的块计数,您实际显示的大型案例不会同时运行。如果需要,您可以回答自己的问题。因为您现在已经为您的案例找到了一个特定的测试示例(m=n=k=256),显示了gemm内核的并发执行,您可以提供该示例作为问题的答案“如何使多个CublasAPI(如cublasDgemm)真正并发执行”。如果您也解释了大小(m=n=k=16)如上所述,我相信这对其他情况也有帮助。如果仔细观察,您会发现两个流中的内核在所有情况下都是重叠的-第二个内核总是在第一个内核完成之前启动。@tera谢谢。是的,您是对的。您能解释一下这种现象吗?这表明并发执行是有效的-第二个内核在第一个内核释放资源后启动,延迟很小。只是由于回答中所述的原因,您没有得到完全重叠。您可以将这些探查器结果与您在同一个流中启动两个DGEMM的情况进行比较,以发现小重叠完全消失。这就是所谓的“尾部效应”,见幻灯片19-23。当内核正在完成并且其线程块正在从GPU中“排空”时,会出现尾部效应,留下越来越多的“空白”。如果将其放入单独的流中,后续的内核启动可以开始利用此空白。谢谢。当前的CUDA API是否支持对等memcpy操作(从一个设备到另一个设备的memcpy)与其他操作同时运行?从书本上看,它不能支持。