Matrix 行大小大于向量宽度时的SIMD转置
您可以找到许多用于转置矩阵的方法,这些矩阵与SIMD指令集的自然大小有关,特别是当一行的大小不超过向量宽度时。例如,SSE中的4x4Matrix 行大小大于向量宽度时的SIMD转置,matrix,transpose,simd,avx,avx2,Matrix,Transpose,Simd,Avx,Avx2,您可以找到许多用于转置矩阵的方法,这些矩阵与SIMD指令集的自然大小有关,特别是当一行的大小不超过向量宽度时。例如,SSE中的4x4float转置,或AVX/AVX2中的4x4double或8x8float转置(AVX-512中的所有内容再次加倍) 但是,当矩阵大于此值时,有哪些选项?例如,使用AVX2的16x16float矩阵?是否可以使用SIMD洗牌来加快速度,或者是聚集+顺序写入是唯一的方法?如果所有矩阵维度都是数据包大小的倍数,则可以按块执行操作,并根据需要交换块。使用SSE2的4x4双
float
转置,或AVX/AVX2中的4x4double
或8x8float
转置(AVX-512中的所有内容再次加倍)
但是,当矩阵大于此值时,有哪些选项?例如,使用AVX2的16x16
float
矩阵?是否可以使用SIMD洗牌来加快速度,或者是聚集+顺序写入是唯一的方法?如果所有矩阵维度都是数据包大小的倍数,则可以按块执行操作,并根据需要交换块。使用SSE2的4x4双矩阵示例:
// transpose vectors i0 and i1 and store the result to addresses r0 and r1
void transpose2x2(double *r0, double* r1, __m128d i0, __m128d i1)
{
__m128d t0 = _mm_unpacklo_pd(i0,i1);
__m128d t1 = _mm_unpackhi_pd(i0,i1);
_mm_storeu_pd(r0, t0);
_mm_storeu_pd(r1, t1);
}
void transpose(double mat[4][4])
{
// transpose [00]-block in-place
transpose2x2(mat[0]+0, mat[1]+0,_mm_loadu_pd(mat[0]+0),_mm_loadu_pd(mat[1]+0));
// load [20]-block
__m128d t20 = _mm_loadu_pd(mat[2]+0), t30 = _mm_loadu_pd(mat[3]+0);
// transpose [02]-block and store it to [20] position
transpose2x2(mat[2]+0,mat[3]+0, _mm_loadu_pd(mat[0]+2),_mm_loadu_pd(mat[1]+2));
// transpose temp-block and store it to [02] position
transpose2x2(mat[0]+2,mat[1]+2, t20, t30);
// transpose [22]-block in-place
transpose2x2(mat[2]+2, mat[3]+2,_mm_loadu_pd(mat[2]+2),_mm_loadu_pd(mat[3]+2));
}
这应该相对容易扩展到其他平方矩阵、其他标量类型和其他体系结构。不是数据包大小的倍数的矩阵可能更复杂(如果它们足够大,可能值得做大多数矢量化工作,只需手动完成最后的行/列)
对于某些大小的矩阵,例如3x4或3x8矩阵,有一些特殊的算法[1]——如果你有一个1003x1003矩阵,你可以在最后的行/列中利用它(可能还有其他奇数大小的算法)
通过一些努力,您还可以为矩形矩阵编写这篇文章(必须考虑如何避免一次缓存多个块,但这是可能的)
锁销演示:
[1] 也许可以将fortran TRANSPOSE intrisic与ISO_C_绑定一起使用,并将其与C链接起来作为子例程或函数调用 转置在fortran中得到了很好的优化
混合语言技能有时也很有用。我甚至将F90与GO链接。谢谢!公平地说,对于大型矩阵(比如100x10000),这仍然会比基于标量或聚集的负载存储提供显著的加速吗?我猜相对加速应该与大小无关(只要没有像缓存这样的效果生效——我想一个具有良好缓存局部性的标量算法将击败一个实现糟糕的块转置)。但可以肯定的是,您需要对此进行基准测试(矩形的情况可能比我最初想的要复杂一些,特别是如果您在编译时不知道大小的话).好的,但是在任何给定的Fortran实现上,它最终运行的是什么asm,使它运行得更快呢?如果我们知道这一点,我们也可以用C和内部函数或手工编写的asm来实现它。gfortran似乎在优化
将
转换为高效的SIMD代码,如果矩阵是平方的,并且大小是四/八的倍数(在编译时就知道了)。对于我研究过的每一个其他情况,我只发现它生成了一个简单的嵌套循环:--而且我没有设法让它在不分配临时变量的情况下生成就地转置(但这可能是由于我有限的fortran技能…)在易于使用的语言中使用功能并不少见。如果混合语言可能不是每个人都喜欢的,但它是相当常见的。对于一个小矩阵,如4x4加倍,调用另一个函数而不是仅仅内联几个SIMD指令的开销非常大。