[英]Matrix operations using code vectorization
我已经编写了一个函数来完成4x4矩阵的转置,但是我不知道如何扩展矩阵m x n的代码。
在哪里可以找到有关使用SSE进行矩阵运算的示例代码? 乘积,转置,逆等?
这是转置4x4的代码:
void transpose(float* src, int n) {
__m128 row0, row1, row2, row3;
__m128 tmp1;
tmp1=_mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src)), (__m64*)(src+ 4));
row1=_mm_loadh_pi(_mm_loadl_pi(row1, (__m64*)(src+8)), (__m64*)(src+12));
row0=_mm_shuffle_ps(tmp1, row1, 0x88);
row1=_mm_shuffle_ps(row1, tmp1, 0xDD);
tmp1=_mm_movelh_ps(tmp1, row1);
row1=_mm_movehl_ps(tmp1, row1);
tmp1=_mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src+ 2)), (__m64*)(src+ 6));
row3= _mm_loadh_pi(_mm_loadl_pi(row3, (__m64*)(src+10)), (__m64*)(src+14));
row2=_mm_shuffle_ps(tmp1, row3, 0x88);
row3=_mm_shuffle_ps(row3, tmp1, 0xDD);
tmp1=_mm_movelh_ps(tmp1, row3);
row3=_mm_movehl_ps(tmp1, row3);
_mm_store_ps(src, row0);
_mm_store_ps(src+4, row1);
_mm_store_ps(src+8, row2);
_mm_store_ps(src+12, row3);
}
这是可用于通过平铺转置NxN矩阵的一种通用方法。 您甚至可以使用现有的4x4转置,并使用4x4瓦片大小:
for each 4x4 block in the matrix with top left indices r, c
if block is on diagonal (i.e. if r == c)
get block a = 4x4 block at r, c
transpose block a
store block a at r, c
else if block is above diagonal (i.e. if r < c)
get block a = 4x4 block at r, c
get block b = 4x4 block at c, r
transpose block a
transpose block b
store transposed block a at c, r
store transposed block b at r, c
else // block is below diagonal
do nothing
endif
endfor
显然,N必须是4的倍数才能起作用,否则,您将需要做一些额外的内务处理。
如上面的注释中所述,很难执行MxN 就地转置-您需要使用其他临时矩阵(有效地使其成为非就地转置)或使用此处描述的方法,但这将使用SIMD进行矢量化变得更加困难。
我不确定如何有效地使用SIMD对任意矩阵进行就地转置,但是我确实知道如何对就地进行转置。 让我描述一下两者
原位转置
对于原位转置,您应该参阅C ++手册中的Agner Fog的Optimizing软件 。 请参见第9.10节“大型数据结构中的缓存争用”示例9.5a。 对于某些矩阵大小,由于缓存别名,您将看到性能大幅下降。 有关示例,请参见表9.1,这为什么转置512x512的矩阵要比转置513x513的矩阵慢得多? 。 Agner提供了一种使用示例9.5b中的循环平铺(类似于Paul R描述的方法)修复此问题的方法。
换位不正确
在这里查看我的答案(投票最多的人) 在C ++中转置矩阵最快的方法是什么? 。 我已经很久没有对此进行研究了,但让我在这里重复我的代码:
inline void transpose4x4_SSE(float *A, float *B, const int lda, const int ldb) {
__m128 row1 = _mm_load_ps(&A[0*lda]);
__m128 row2 = _mm_load_ps(&A[1*lda]);
__m128 row3 = _mm_load_ps(&A[2*lda]);
__m128 row4 = _mm_load_ps(&A[3*lda]);
_MM_TRANSPOSE4_PS(row1, row2, row3, row4);
_mm_store_ps(&B[0*ldb], row1);
_mm_store_ps(&B[1*ldb], row2);
_mm_store_ps(&B[2*ldb], row3);
_mm_store_ps(&B[3*ldb], row4);
}
inline void transpose_block_SSE4x4(float *A, float *B, const int n, const int m, const int lda, const int ldb ,const int block_size) {
#pragma omp parallel for
for(int i=0; i<n; i+=block_size) {
for(int j=0; j<m; j+=block_size) {
int max_i2 = i+block_size < n ? i + block_size : n;
int max_j2 = j+block_size < m ? j + block_size : m;
for(int i2=i; i2<max_i2; i2+=4) {
for(int j2=j; j2<max_j2; j2+=4) {
transpose4x4_SSE(&A[i2*lda +j2], &B[j2*ldb + i2], lda, ldb);
}
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.