我正在尝试计算C = A * A'在使用cuBLAS的GPU上,我发现rank-k update cublasDsyrk
的运行速度比一般矩阵 - 矩阵乘法例程cublasDgemm
慢约5倍。
这让我感到惊讶;我认为syrk
会更快,因为它是一段更专业的代码。这是不合理的期望吗?我做错了吗?
最终,我正在编写CUDA代码以编译成MATLAB的MEX文件,所以请不要提供完整的工作示例(将会有很多与MATLAB对象争论的无关代码)。
我知道这可能不是最好的方式,但是我使用clock()
计算代码运行的时间:
// Start of main function
clock_t tic = clock();
clock_t toc;
/* ---- snip ---- */
cudaDeviceSynchronize();
toc = clock();
printf("%8d (%7.3f ms) Allocated memory on GPU for output matrix\n",
toc-tic,1000*(double)(toc-tic)/CLOCKS_PER_SEC);
// Compute the upper triangle of C = alpha*A*A' + beta*C
stat = cublasDsyrk(handle, CUBLAS_FILL_MODE_UPPER, CUBLAS_OP_N,
M, N, &alpha, A, M, &beta, C, M);
toc = clock();
printf("%8d (%7.3f ms) cublasDsyrk launched\n",
toc-tic,1000*(double)(toc-tic)/CLOCKS_PER_SEC);
cudaDeviceSynchronize();
toc = clock();
printf("%8d (%7.3f ms) cublasDsyrk completed\n",
toc-tic,1000*(double)(toc-tic)/CLOCKS_PER_SEC);
/* ----- snip ----- */
输出,在[12 x 500,000]随机矩阵(列主存储)上运行:
911 ( 0.911 ms) Loaded inputs, initialized cuBLAS context
1111 ( 1.111 ms) Allocated memory on GPU for output matrix
1352 ( 1.352 ms) cublasDsyrk launched
85269 ( 85.269 ms) cublasDsyrk completed
85374 ( 85.374 ms) Launched fillLowerTriangle kernel
85399 ( 85.399 ms) kernel completed
85721 ( 85.721 ms) Finished and cleaned up
用
替换syrk
来电后
stat = cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_T, M, M, N,
&alpha, A, M, A, M, &beta, C, M);
整个事情运行得更快:
664 ( 0.664 ms) Loaded inputs, initialized cuBLAS context
796 ( 0.796 ms) Allocated memory on GPU for output matrix
941 ( 0.941 ms) cublasDgemm launched
16787 ( 16.787 ms) cublasDgemm completed
16837 ( 16.837 ms) Launched fillLowerTriangle kernel
16859 ( 16.859 ms) kernel completed
17263 ( 17.263 ms) Finished and cleaned up
我尝试了几个其他尺寸的矩阵;有趣的是,当矩阵行数很少时,速度差异似乎最明显。在100行时,gemm
仅快2倍,在1000行时它稍慢(这是我一直期望的)。
我使用的是CUDA Toolkit 7.5,GPU设备是NVIDIA Grid K520(Kepler,计算能力3.0)。我在Amazon EC2 g2.x2large实例上运行。
答案 0 :(得分:1)
[n×500,000]都是非常宽矩阵。在这些极端情况下,gemm()
和syrk()
可能无法达到其最高性能,其中syrk()
比gemm()
快两倍(因为结果矩阵是同步的,因此你可以节省一半的计算量。)
另一个考虑因素是CUDA gemm()
/ syrk()
通常将矩阵划分为固定大小的子矩阵作为实现高性能的基本计算单元。对于dgemm()
,子矩阵的大小最多可达32x64,如以下链接所示。
http://www.netlib.org/lapack/lawnspdf/lawn267.pdf
如果您的尺寸(12或100)既不大于子矩阵也不是其倍数,性能通常会下降很多。