我想知道SSE2如何加速矩阵乘法
这是我的代码
int mat_mult_simd(double *a, double *b, double *c, int n)
{
__m128d c1,c2,a1,a2,b1;
for(int i=0; i<n/2; i++){
for(int j=0; j<n/2; j++){
c1 = _mm_load_pd(c+(2*j*n)+(i+2));
c2 = _mm_load_pd(c+n+(2*j*n)+(i+2));
for(int k=0; k<n; k++){
a1 = _mm_load1_pd(a+k+(2*j*n));
a2 = _mm load1_pd(a+n+k+(2*j*n));
b1 = _mm_load_pd(b+(k*n)+(i*2));
c1 = _mm_add_pd(c1, _mm_mul_pd(a1,b1));
c2 = _mm_add_pd(c2, _mm_mul_pd(a2,b1));
}
__mm_store_pd(c+(2*j*n)+(i+2), c1);
__mm_store_pd(c+n+(2*j*n)+(i+2), c2);
}
}
return 0;
}
每个参数意味着
'a'=向量a(MAT_SIZE * MAT_SIZE)
'b'=向量b(MAT_SIZE * MAT_SIZE)
'c'=向量c(MAT_SIZE * MAT_SIZE)
'n'= MAT_SIZE是常数(它总是偶数且&gt; = 2)
这段代码加速了X4。对
int mat_mult_default(double *a, double *b, double *c, int n)
{
double t;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
t=0.0;
for(int k=0; k<n; k++)
t += a[i*n+k] * b[k*n+j];
c[i*n+j] = t;
}
}
}
但我想加快速度。我通常试验MAT_SIZE 1000 * 1000或2000 * 2000。 我怎么能加快速度?还有其他方法可以编制索引吗?我真的很想知道。感谢。
答案 0 :(得分:1)
你可以做一些事情。显而易见的是将工作分成几个线程(每个核心1个)。您可以使用OpenMP(最简单),Intel TBB或其他多线程库。 这将为多核机器提供重大改进。
另一件事是查看反汇编(通过您最喜欢的调试器) - 看看编译器如何处理您用于索引的所有乘法,其中一些可以被消除。
您的代码在一个循环中执行2次计算,尝试执行更多4或8以获得更好的位置。例如。 a1和a2可以与已经在L1缓存中的邻居一起计算。您实际上可以通过一次加载操作加载它们。
确保各种数组是SSE对齐的(16字节)并更改代码以使用对齐的读/写。
我将多线程留在最后,因为发现错误更难。
答案 1 :(得分:-1)
只需使用正确的库,如英特尔数学核心库或类似的高度优化的线性代数包(OpenBLAS,AMD核心数学库,ATLAS,......)。与手写代码相比,它们被认为更快。它们有时甚至针对指令集和高速缓存大小进行特定于处理器的优化。他们是各自领域的专业人士。除非您打算发表关于自己优化的论文,否则请使用库。
在德国计算机杂志的latest issue中,他们声称编译器足够聪明,可以单独使用SSE或AVX。只需编写正确的循环,自动矢量化器将带来最佳效果。这适用于最新的英特尔编译器。微软的编译器太垃圾了。在某些情况下,如果使用正确的编译器标志,英特尔的编译器甚至会检测到您编写矩阵乘法并通过正确的调用替换它。或者你必须检查文档,学习这样的软件包并不难。