对于个人和有趣的事情,我使用SSE(4.1)编写geom lib。
在处理行主要与列主要存储矩阵时,我花费最后12小时试图了解性能问题。
我知道Dirext / OpenGL矩阵存储在行专业中,所以最好让我的矩阵按行主顺序存储,这样在向/从GPU /着色器存储/加载矩阵时我就没有转换。
但是,我做了一些分析,并且我用colomun major得到了更快的结果。
要使用行major中的transfrom矩阵变换点,它是P'= P * M.而在列major中,它是P'= M * P. 所以在Column major中它只是4点积,所以在Row专业时只需要4个SSE4.1指令(_mm_dp_ps)我必须在转置矩阵上做那4个点积。
10M载体的性能结果
(30/05/2014 @ 08:48:10)日志:[5](Vec.Mul.Matrix)= 76.216653 ms(行主变换)
(30/05/2014 @ 08:48:10)记录:[6](Matrix.Mul.Vec)= 61.554892 ms(列主要变换)
我尝试了几种使用_MM_TRANSPOSE进行Vec * Matrix操作的方法,我发现的最快方法是:
mssFloat Vec4::operator|(const Vec4& v) const //-- Dot Product
{
return _mm_dp_ps(m_val, v.m_val, 0xFF ).m128_f32[0];
}
inline Vec4 operator*(const Vec4& vec,const Mat4& m)
{
return Vec4( Vec4( m[0][0],m[1][0],m[2][0],m[3][0]) | vec
, Vec4( m[0][1],m[1][1],m[2][1],m[3][1]) | vec
, Vec4( m[0][2],m[1][2],m[2][2],m[3][2]) | vec
, Vec4( m[0][3],m[1][3],m[2][3],m[3][3]) | vec
);
}
我的类Vec4只是一个__m128 m_val,在优化的C ++中,矢量结构全部在SSE寄存器上完成。
我的第一个猜测是,这种乘法不是最优的。我是SSE的新手,所以我有点困惑如何优化这个,我的直觉告诉我使用shuffle指令,但我想理解为什么它会更快。它会比分配更快地加载4 shuffle __m128(__m128 m_val = _mm_set_ps(w,z,y,x);)
来自https://software.intel.com/sites/landingpage/IntrinsicsGuide/ 我在mm_set_ps上找不到性能信息
编辑:我仔细检查分析方法,每个测试都以相同的方式完成,因此没有内存缓存的差异。为了避免本地缓存,我正在对随机化的bug向量数组进行操作,每个测试的种子都是相同的。每次执行时只有1次测试,以便从内存缓存增加avoir性能。答案 0 :(得分:3)
不要将_mm_dp_ps
用于矩阵乘法!我已经在Efficient 4x4 matrix vector multiplication with SSE: horizontal add and dot product - what's the point?详细解释了这一点(顺便说一句,这是我关于SO的第一篇文章)。
除了SSE之外,你不需要任何其他任何东西来有效地做到这一点(甚至不是SSE2)。使用此代码可以有效地执行4x4矩阵乘法。如果矩阵以行主要顺序存储而不是gemm4x4_SSE(A,B,C)
。如果矩阵按列主要顺序存储,而不是gemm4x4_SSE(B,A,C)
。
void gemm4x4_SSE(float *A, float *B, float *C) {
__m128 row[4], sum[4];
for(int i=0; i<4; i++) row[i] = _mm_load_ps(&B[i*4]);
for(int i=0; i<4; i++) {
sum[i] = _mm_setzero_ps();
for(int j=0; j<4; j++) {
sum[i] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(A[i*4+j]), row[j]), sum[i]);
}
}
for(int i=0; i<4; i++) _mm_store_ps(&C[i*4], sum[i]);
}
答案 1 :(得分:0)
我们实际上分析了3x4矩阵伪乘法(如果它是4x4仿射)并且发现在SSE3和AVX中,列主要与行主要布局之间的差异非常小(<10%)只要两者都被优化到极限。