我需要一个关于如何在CUDA中优化我的Smith-Waterman算法实现的建议。
我想要优化的部分是填充矩阵。由于矩阵元素之间的数据依赖性(每个下一个元素依赖于其他元素 - 离开它,直到它,并且离开它),我并行地填充反对角矩阵元素,如图所示如下图所示:
我的程序在循环中运行
int diag = 1;
for(int x = 0; x < size_b; x++)
{
block_size = 1024;
if(block_size > diag)
{
block_size = diag;
}
SAFE_KERNEL_CALL((dev_init_diag<<<(diag - 1)/block_size + 1, block_size>>>(H, size_a, size_b, x,
sequence_a, sequence_b, false, x_offset, y_offset, null_ind)));
diag++;
}
如您所见,每个对角线都有一个内核调用。
由于我有相当大的矩阵(侧面有21000
个元素),因此有很多内核调用。因此,我对CUDA内核调用的开销很大,浪费了大约一半的处理时间,可以通过Visual Profiler的截图看到(查看Profiler开销字符串):
所以,问题是如何摆脱多个内核调用并消除这种开销。
有一件重要的事情需要注意: 我为每个对角线调用新内核的原因是我需要在下一次调用之前同步线程和块,并且据我所知,有一种方法可以同步CUDA块 - 完成内核并再次启动它。然而,对于这种算法,可能有更好的解决方案。
感谢您阅读本文!
/////////////////////////////////////////////// ///////////////
好的,谢谢你的回复! 还有一个问题,更多关于CUDA: 所以,我必须实现一个新内核,可能是这样的:__global__ void kernel(...)
{
for(int diag_num = 0; diag_num < size; diag_num++)
{
init_one_diag(...);
syncronize_threads();
}
}
但这意味着我必须只在一个cuda-block上启动这个内核?(因为我知道不同的块之间没有同步)
在我以这种方式启动内核之前:
dev_init_diag&lt;&lt;&lt;(diag - 1)/ block_size + 1,block_size&gt;&gt;&gt;(...)
会有效的新方法吗?
答案 0 :(得分:3)
我建议通过现有的文献来实现Smith-Waterman算法矩阵填充问题的有效方法。
从代码的描述中,您选择并行填充反对角线,并为每个反对角线启动一个内核。正如您所提到的,由于多个内核启动,这非常无效。
一个简单的替代方案是构建一个负责计算所有反对角线的单个核函数。应使用至少等于最长反对角线的多个线程启动此内核。内核执行的迭代次数等于要计算的反对角线的数量。对于比最长的对角线短的反对角线,只有一部分线程保持活动状态。
中描述了这种方法Parallelizing the Smith-Waterman Local Alignment Algorithm using CUDA
但由于两个原因无效:
中的方法提供了反对角矩阵填充的替代方法
Acceleration of the Smith–Waterman algorithm using single and multiple graphics processors
在此示出了如何重新构造Smith-Waterman(反对角线)矩阵填充算法,以便可以一次并行地在一行(或列)中执行计算。强调了行(或列)计算如何允许GPU存储器访问连续(合并)并因此快速。虽然没有明确提到,但我相信这种方法可以缓解(或完全消除)上述非活动线程的问题。
修改强>
GPU Computing Gems一书,Emerald Edition,给Smith-Waterman算法分两章,即
第11章,使用Smith-Waterman算法精确扫描序列数据库
和
第13章,GPU-超级计算机加速模式匹配
后者是第二个提到的方法的同一作者的一章。前者包含对优化的CUDA代码的逐步推导,这可能对未来的用户有用。