SIMD优化之Scalar Waving

image.png

SIMD,Single Instruction Multiple Data,单指令多数据。

假设上图中的PU都为加法器,那么,当从指令存储取出来一条加法指令的时候,将这条加法指令同时给到四个加法器,加法器再去数据存储取出各自的数据做加法运算。

SIMD的指令举例如下:

add.4 arr1 arr2

假设,

arr1 = {1, 2, 3, 4};
arr2 = {2, 3, 4, 5};

那么,期望的执行结果为{3, 5, 7, 9}

但如果arr1arr2的取值是下面这种情况,

arr1 = {1, 1, 1, 1};
arr2 = {2, 2, 2, 2};

此时,期望的执行结果为{3, 3, 3, 3}

可以看到,这时四个加法器做的操作一模一样,都是1 + 2 = 3. 那能不能提前检测这种情况并做好标记,在执行的时候,根据这个标记来决定启用一个加法器还是四个?

这完全是可行的!

在编译阶段就能检测到很多类似这样的指令,比如:

mov i <- 0
add i <- i + 1

上面两条指令在编译器阶段就都能知道属于上述类型的指令。当然也可以在硬件做。

原先需要四个加法器同时工作,而现在只需要一个加法器工作即可,节省了功耗。

在这基础之上,我们可以更进一步优化...

现在有三个加法器是不工作的,我们可以想办法把他们利用起来。假设指令执行情况是这样:

w0.add.4 arr1 arr2
w1.add.4 arr1 arr2
...

那么,w0.add.4执行在第一个加法器上,与此同时,可以让w1.add.4执行在第二个加法器上,如此便提升了处理器的执行效率。

按照上述的合并SIMD中空闲Lane的思路,我们可以进一步应用到分支处理的情形,优化存在分支指令时的执行效率。

例如,指令A的执行Mask为0b1110,即Lane0不执行,其他三个Lane都执行。下一条指令B的执行Mask为0b0001,即Lane0执行,其他三个Lane都不执行。那么可以把两条指令合并到一起。执行指令A时没有启用的Lane0用来执行指令B。这样便提升了执行效率。

如果把上述两种优化方法结合使用,执行效率便会提升更多。

image.png

以上内容参考自《Scalar Waving: Improving the Efficiency of SIMD Execution on GPUs》,该文还给出了具体实现细节,有兴趣的可以进一步阅读。

你可能感兴趣的:(SIMD优化之Scalar Waving)