Optimization 使用SSE、AVX和OpenMP进行快速内存转置
在C/C++中,我需要一个用于高斯卷积函数的快速内存转置算法。我现在做的是Optimization 使用SSE、AVX和OpenMP进行快速内存转置,optimization,openmp,sse,transpose,avx,Optimization,Openmp,Sse,Transpose,Avx,在C/C++中,我需要一个用于高斯卷积函数的快速内存转置算法。我现在做的是 convolute_1D transpose convolute_1D transpose 结果表明,使用这种方法时,滤波器尺寸必须大(或比我预期的大),或者转置时间比卷积时间长(例如,对于1920x1080矩阵,卷积时间与滤波器尺寸为35的转置时间相同)。我目前使用的转置算法使用循环阻塞/平铺以及SSE和OpenMP。我尝试了一个使用AVX的版本,但没有更快。有没有关于我如何加快速度的建议 inline void t
convolute_1D
transpose
convolute_1D
transpose
结果表明,使用这种方法时,滤波器尺寸必须大(或比我预期的大),或者转置时间比卷积时间长(例如,对于1920x1080矩阵,卷积时间与滤波器尺寸为35的转置时间相同)。我目前使用的转置算法使用循环阻塞/平铺以及SSE和OpenMP。我尝试了一个使用AVX的版本,但没有更快。有没有关于我如何加快速度的建议
inline void transpose4x4_SSE(float*A,float*B,const int lda,const int ldb){
__m128第1行=_mm_load_ps(&A[0*lda]);
__m128第2行=_mm_load_ps(&A[1*lda]);
__m128第3行=_mm_load_ps(&A[2*lda]);
__m128第4行=_mm_load_ps(&A[3*lda]);
_MM_TRANSPOSE4_PS(第1行、第2行、第3行、第4行);
_mm_商店(第1行为&B[0*ldb]);
_mm_商店(和B[1*ldb],第2行);
_mm_store_ps(&B[2*ldb],第3行);
_mm_store_ps(&B[3*ldb],第4行);
}
//块大小=16效果最佳
内联无效转置块(浮点*A、浮点*B、常数整数n、常数整数m、常数整数lda、常数整数ldb、常数整数块大小){
#pragma-omp并行
对于(int i=0;i,我想最好的办法是尝试将卷积和转置结合起来,即在转置过程中写出卷积转置的结果。几乎可以肯定的是,转置过程中内存带宽有限,因此减少转置过程中使用的指令数量并没有真正的帮助(因此使用AVX缺乏改进)。减少数据传输次数将为您带来最好的性能改进。FWIW,在3年前的i7M笔记本电脑核心CPU上,这种朴素的4x4转置几乎比您的SSE版本慢,而在更新的Intel Xeon E5-2630 v2@2.60GHz桌面CPU上则快了近40%
inline void transpose4x4_naive(float*A,float*B,const int lda,const int ldb){
常量float r0[]={A[0],A[1],A[2],A[3]};//是否改为memcpy?
A+=lda;
常量浮点r1[]={A[0],A[1],A[2],A[3]};
A+=lda;
常量浮点r2[]={A[0],A[1],A[2],A[3]};
A+=lda;
常量浮点r3[]={A[0],A[1],A[2],A[3]};
B[0]=r0[0];
B[1]=r1[0];
B[2]=r2[0];
B[3]=r3[0];
B+=ldb;
B[0]=r0[1];
B[1]=r1[1];
B[2]=r2[1];
B[3]=r3[1];
B+=ldb;
B[0]=r0[2];
B[1]=r1[2];
B[2]=r2[2];
B[3]=r3[2];
B+=ldb;
B[0]=r0[3];
B[1]=r1[3];
B[2]=r2[3];
B[3]=r3[3];
}
奇怪的是,老式笔记本电脑的CPU速度比双核E5-2630 v2台式机快两倍,但情况不同:)
否则,您可能也会感兴趣(需要立即登录…考虑一下这个4x4转置
struct MATRIX {
union {
float f[4][4];
__m128 m[4];
__m256 n[2];
};
};
MATRIX myTranspose(MATRIX in) {
// This takes 15 assembler instructions (compile not inline),
// and is faster than XMTranspose
// Comes in like this 1 2 3 4 5 6 7 8
// 9 10 11 12 13 14 15 16
//
// Want the result 1 5 9 13 2 6 10 14
// 3 7 11 15 4 8 12 16
__m256 t0, t1, t2, t3, t4, t5, n0, n1;
MATRIX result;
n0 = in.n[0]; // n0 = 1, 2, 3, 4, 5, 6, 7, 8
n1 = in.n[1]; // n1 = 9, 10, 11, 12, 13, 14, 15, 16
t0 = _mm256_unpacklo_ps(n0, n1); // t0 = 1, 9, 2, 10, 5, 13, 6, 14
t1 = _mm256_unpackhi_ps(n0, n1); // t1 = 3, 11, 4, 12, 7, 15, 8, 16
t2 = _mm256_permute2f128_ps(t0, t1, 0x20); // t2 = 1, 9, 2, 10, 3, 11, 4, 12
t3 = _mm256_permute2f128_ps(t0, t1, 0x31); // t3 = 5, 13, 6, 14, 7, 15, 8, 16
t4 = _mm256_unpacklo_ps(t2, t3); // t2 = 1, 5, 9, 13, 3, 7, 11, 15
t5 = _mm256_unpackhi_ps(t2, t3); // t3 = 2, 6, 10, 14, 4, 8, 12, 16
result.n[0] = _mm256_permute2f128_ps(t4, t5, 0x20); // t6 = 1, 5, 9, 13, 2, 6, 10, 14
result.n[1] = _mm256_permute2f128_ps(t4, t5, 0x31); // t7 = 3, 7, 11, 15, 4, 8, 12, 16
return result;
}
好的方面。我之所以要做转置,首先是因为读取不连续的数据会导致大量缓存命中。因此,缓存命中和进行转置的时间之间有一场斗争。我不确定写出转置的结果是否有助于卷积。不过,可能我必须想出一个不同的方法L较小过滤器大小的算法。我将对适合二级或三级缓存的较小矩阵大小进行一些测试,然后返回给您。在本例中,您可能对AVX没有更好表现的原因是正确的。我在64x64、192x192、896x896和5008x5008上尝试了转置。这些应该对应于我的l1、L2、L3和主内存区域。AVX对于64x64(一级缓存),它仅略优于SSE。我在测试中关闭了OpenMP。您正在通过2个1D过程(可分离的高斯模糊)进行2D高斯卷积?为什么在第一次读取数据时数据不连续?似乎您应该能够读取水平模糊过程的连续数据,并写出转置后的结果,以便在第二次垂直过程中再次连续读取,并将转置后的结果写回原始布局。这也可能值得一看这是在GPU上快速实现的。你可以在块中一次完成完整的2D模糊-在大约一级缓存大小的块中读取整个2D内核输入区域,然后在块上进行两次可分离的1D传递,并写入一个单独的输出图像。好吧,该链接已经失效。我想我已经找到了新链接,并在上面对其进行了更新,但需要进行更新ogin now.Haswell只有一个洗牌单元,但Nehalem通过IvB有两个,洗牌吞吐量为每0.5c一个。(.)E5 xxxx v2是IvyBridge,所以可能不是这样。除非你使用多线程运行,否则我想知道为什么你在比较笔记本电脑和Xeon时会提到内核计数。\uUm128 xmm[4]
\uuuuum256ymm[2]
将是更为明显的名称。但这种结合是一种非常丑陋的黑客行为,如果你滥用它,很容易编译成低效的代码。