首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >基于AVX的平铺矩阵乘法

基于AVX的平铺矩阵乘法
EN

Stack Overflow用户
提问于 2019-11-23 16:15:44
回答 1查看 1.6K关注 0票数 3

我编写了下面的C函数,用平铺/分块和AVX向量相乘两个NxN矩阵,以加快计算速度。但是现在,当我尝试将AVX的本质和瓷砖结合起来时,我得到了一个分割错误。知道为什么会这样吗?

此外,是否有一个更好的内存访问模式的矩阵B?也许是先换位,甚至改变k和j循环?因为现在,我正在按列遍历它,这在空间局部性和缓存线方面可能不是很有效。

代码语言:javascript
运行
复制
  1 void mmult(double A[SIZE_M][SIZE_N], double B[SIZE_N][SIZE_K], double C[SIZE_M][SIZE_K])
  2 {
  3   int i, j, k, i0, j0, k0;
  4   // double sum;
  5   __m256d sum;
  6   for(i0 = 0; i0 < SIZE_M; i0 += BLOCKSIZE) {
  7   for(k0 = 0; k0 < SIZE_N; k0 += BLOCKSIZE) {
  8   for(j0 = 0; j0 < SIZE_K; j0 += BLOCKSIZE) {
  9       for (i = i0; i < MIN(i0+BLOCKSIZE, SIZE_M); i++) {
 10         for (j = j0; j < MIN(j0+BLOCKSIZE, SIZE_K); j++) {
 11           // sum = C[i][j];
 12           sum = _mm256_load_pd(&C[i][j]);
 13           for (k = k0; k < MIN(k0+BLOCKSIZE, SIZE_N); k++) {
 14             // sum += A[i][k] * B[k][j];
 15             sum = _mm256_add_pd(sum, _mm256_mul_pd(_mm256_load_pd(&A[i][k]), _mm256_broadcast_sd(&B[k][j])));
 16           }
 17           // C[i][j] = sum;
 18           _mm256_store_pd(&C[i][j], sum);
 19         }
 20       }
 21   }
 22   }
 23   }
 24 }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-11-23 19:44:01

_mm256_load_pd是一个对齐所需的加载,但您只需要按k++执行,而不是在最内循环中加载4倍的32字节向量的k+=4。所以它会发生故障,因为每4种负载中就有3种不对齐。

您不希望执行重叠加载,真正的错误是索引;如果您的输入指针是32字节对齐的,您应该能够继续使用_mm256_load_pd而不是_mm256_loadu_pd。因此,使用_mm256_load_pd成功地捕获了您的错误,而不是工作,但是给出了错误的数字结果。

将四个row*column C[i][j+0..3] 点产品矢量化(以生成C[i][j+0..3]向量)的策略是,应该从4个不同的列加载4个连续的双打(通过B[k][j]的向量加载来加载B[k][j+0..3]),并从A[i][k]中播放1个double。记住,你想要4个点的产品并行。

另一种策略可能会涉及到最后的水平和,直到标量C[i][j] += horizontal_add(__m256d),但我认为这需要先转置一个输入,这样行向量和列向量都在一个点积的连续内存中。但是,在每个内环的末尾,你需要进行水平和的洗牌。

您可能还希望至少使用2个sum变量,这样就可以同时读取整个缓存行,并将FMA延迟隐藏在内部循环中,并有望成为吞吐量的瓶颈。或更好地并行处理4或8个向量。使您生成C[i][j+0..15]作为sum0sum1sum2sum3。(或者使用__m256d数组;编译器通常会完全展开8的循环,并将数组优化为寄存器。)

我认为您只需要5个嵌套循环,就可以阻塞行和列。虽然很明显,6个嵌套循环是一个有效的选项:参见loop tiling/blocking for large dense matrix multiplication,它在问题中有一个5嵌套循环,但在一个答案中有一个6嵌套循环。(只是标量,而不是矢量化)。

在这里,除了行*列点产品策略之外,可能还有其他错误,我不确定。

如果您使用的是AVX,您可能也想使用FMA,除非您需要运行在Sandbybridge/Ivybridge和AMD推土机上。(河床和后来有FMA3)。

其他matmul策略包括在内循环中添加目标,这样您就可以在内环中加载CA,并从B加载。(或者B和A交换了,我忘记了。) What Every Programmer Should Know About Memory?有一个矢量化缓存阻塞的例子,在附录中是这样工作的,用于SSE2 __m128d向量。https://www.akkadia.org/drepper/cpumemory.pdf

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59009628

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档