我有一个CUDA代码,我想优化它。我的内核正在使用dim3 grid=(35,48)
和dim3 threads=(18,18)
。首先,每个块执行独立的290向量计算,其中每个线程执行1个向量计算(其为1024次加法 - 乘法)。
但是,此计算的前17 * 17 = 289的输入数据存储在共享数组im1中,最后一个数据存储在im2中(输出数组也不同)。之后,我使用所有获得的数据进行进一步的计算。
我按如下方式实施:
if ((threadIdx.x < 17) && (threadIdx.y < 17)){
**instructions for 289s vector calculations**
}
else if ((threadIdx.x == 17) && (threadIdx.y == 17)){
**instruction for 290 vector calculation**
}
__syncthreads();
***further calculations***
所以,如果我理解正确,我的第一个289跟随1分支,而#324跟随另一个分支。只要第一组线程在warp#0,1,..,10中,并且线程#324在warp#11中,就没有发散的分支。但是,我读到,通常最好避免在这些内核中使用任何if
语句,并用strided索引替换它们,或类似的东西。那么,我能以某种方式改进这些代码吗?
我的GPU是带有cc 5.2的GTX 980,我使用VS2013进行编码。
谢谢,米哈伊尔
答案 0 :(得分:1)
让我们考虑一个18 * 18个线程的块,编号从0(0,0)到323(17,17)。
So, if I understand correct, my first 289 follow 1 branch [...]
如果通过&#34;第289&#34;你指的是从0(0,0)到288(16,16)编号的线程,然后是no,并非所有线程都采用第一个分支。例如,线程17(0,17)不采用分支(参见下图)。然而,在一个块的范围内,289个线程确实占用了该分支。
[...] and thread #324 follows another
这是正确的,线程323(17,17)采用第二个分支。
线程17(0,17),35(1,17)...... 305(16,17)和306(17,0),307(17,1)...... 322(17,16)(总共35个线程)不占用任何分支并且浪费了。从表现的角度来看,这很糟糕,但也不是灾难性的。
但请考虑以下您正在做的模式:
0 1 2 … 15 16 17
0 * * * * * * - * represents a thread that takes branch 1
1 * * * * * * - X represents a thread that takes branch 2
2 * * * * * * - - represents a thread that takes no branch
… * * * * * * -
15 * * * * * * -
16 * * * * * * -
17 - - - - - - X
请记住,warp由32个线程组成。所以线程0..31,32..63等以锁步执行。您可能会注意到上面的架构,每18个线程有一个非活动线程。换句话说,这意味着所有你的warp发散。
但是,它可能不是一个巨大的性能影响(如果有的话),因为其中一个分支总是&#34;什么都不做&#34;。话虽这么说,我肯定会鼓励你修改你的设计,我相信你可以注意到性能的提高(更多是由于内存访问模式而不是分歧本身)。一个显而易见的解决方案是仅启动290个线程而不是324个线程,并自行映射到x和y坐标,但是最后一个warp会以明显的方式发散。
另一个解决方案是启动足够的warp来覆盖前289个线程(这意味着10个warp,最后一个浪费31个线程)并运行一个补充warp,其中你使用一个线程用于第二个分支(最后一个,例如)。这将是11次经线,352次线程,62次浪费。这在效率方面可能看起来更糟,但由于存储器访问模式,它实际上比这更复杂,所以试一试。
另请注意,如果if/else
语句的主体在代码上实际上没有差异,但在数据上(因为您似乎暗示......),那么使用分支是没有意义的。只是玩指针。可能会出现其他问题(与内存访问合并有关),但不存在代码流分歧。
我建议进行更多改进,但如果没有看到您的代码或了解您的数据是如何布局的,那么它就是在黑暗中拍摄的。你在评论中说,你不能让NSIGHT工作:我强烈建议你把它作为优先事项。
答案 1 :(得分:0)
按照我的理解,如果要优化分支,数据必须提前处理(即那些位于18号的数据要提前聚在一起,在原来的位置删除)。