【发布时间】:2011-11-10 08:12:43
【问题描述】:
我通过细分输入矩阵 (A[x/num_of_streams*y]B[xy] = C[ 在单个 GPU (Tesla C2050) 上的不同流上运行 CUBLAS v2.0 x/num_of_streams*y]),但不知何故,当我使用 CUDA 流时,它会花费更多时间。这是代码sn-p:
//plan is a struct containing the matrix dimensions and stream numbers
//parallel in nstreams - should be! MAX 16 streams could run concurrently
//Copy A - cudaMemCpyAsync
for(i = 0; i < nstreams; i++)
cudgemm_copyA_in_streams (&plan[i]);
//Copy B - cudaMemCpyAsync
for(i = 0; i < nstreams; i++)
cudgemm_copyB_in_streams (&plan[i]);
//Create handles - serial
for(i = 0; i < nstreams; i++)
handle[i] = create_handle();
//Run kernels - first doing a cublasSetStream(handle, plan->stream) before running cublasDgemm...
for(i = 0; i < nstreams; i++)
cudgemm_kernel_in_streams (&plan[i], handle[i], 1.0f, 1.0f);
//Destroy handles - serial
for(i = 0; i < nstreams; i++)
destroy_handle (handle[i]);
//Copy C - cudaMemCpyAsync
for(i = 0; i < nstreams; i++)
cudgemm_copyC_in_streams (&plan[i]);
//EDIT: Function body
//The other two copy functions are exactly the same as this
void cudgemm_copyA_in_streams(TGPUplan *plan)
{
cudasafe(cudaMemcpyAsync(plan->Ad_Data, plan->Ah_Data, (plan->Acols * plan->Arows * sizeof(double)), cudaMemcpyHostToDevice, plan->stream) );
}
//Create handle
cublasHandle_t create_handle ()
{
cublasHandle_t handle;
checkError(cublasCreate(&handle), "cublasCreate() error!\n");
return handle;
}
//Destroy handle
void destroy_handle (cublasHandle_t handle)
{
checkError(cublasDestroy(handle), "cublasDestroy() error!\n");
}
//Kernel
void cudgemm_kernel_in_streams(TGPUplan *plan, cublasHandle_t handle, const double alpha, const double beta)
{
cublasStatus_t ret;
cublasSetStream(handle, plan->stream);
ret = cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, plan->Arows, plan->Ccols, plan->Acols, &alpha, plan->Ad_Data, plan->Arows, plan->Bd_Data, plan->Brows, &beta, plan->Cd_Data, plan->Crows);
checkError(ret, "cublas Dgemm returned an error!\n");
}
所以我在流之间来回切换并分配工作,期望获得更好的执行时间,但我注意到与不使用流的版本相比,流的数量越多,程序花费的时间就越多。我哪里错了? 交叉发帖到 Nvidia 论坛 - http://forums.nvidia.com/index.php?showtopic=209420
编辑:
我修改我的程序如下:
//copy data
for(i = 0; i < nstreams; i++)
{
cudgemm_copyA_in_streams (&plan[i]);
cudgemm_copyB_in_streams (&plan[i]);
}
//Run kernel and copy back
for(i = 0; i < nstreams; i++)
{
cudgemm_kernel_in_streams (&plan[i], handle[i], 1.0f, 1.0f);
cudgemm_copyC_in_streams (&plan[i]);
}
当我针对 6144 的矩阵顺序分析我的程序时,我得到以下信息:
Kernel time = 42.75 % of total GPU time
Memory copy time = 28.9 % of total GPU time
Kernel taking maximum time = fermiDgemm_v2_kernel_val (42.8% of total GPU time)
Memory copy taking maximum time = memcpyHtoDasync (21.7% of total GPU time)
Total overlap time in GPU = 65268.3 micro sec. (3.6% of total GPU time)
当我为上述循环计时时,我得到的时间为 0.000284 秒,而不使用流的版本为 1.703289 秒(在该版本中,我也为两个顺序内存副本、内核调用和剩余的 memCpy 计时) . 我想,由于我没有使用任何同步结构,可能是我打印了计算实际完成之前的时间(我发现很难相信有 100% 的改进)。
【问题讨论】:
-
代码中有太多抽象,无法说明原因,但我猜是内存副本。您的设备有 2 个 DMA 引擎,它可以将内核执行与最多 2 个流上的异步内存传输重叠,或者执行单个双向传输。盲目地排队 16 次传输并不是提高性能的秘诀。您可以发布您的一种复制方法的代码吗?
-
我还没有到 16 个流,但我正在测试 2、4、8 个流。谢谢提醒我引擎的数量...但是第三个副本在内核执行后生效,也就是前两个副本完成之后,所以我复制C时DMA引擎应该是空闲的?