矩阵乘法:为什么非阻塞优于阻止?

时间:2016-07-04 17:49:50

标签: c caching matrix-multiplication cpu-architecture

我正在尝试通过阻止循环来提高缓存性能来加速矩阵乘法算法,但无论矩阵大小,块大小如何,非阻塞版本仍然明显更快(我尝试了2和2之间的大量值) 200,潜力2和其他)和优化水平。

非阻止版本:

select distinct * 
from mytable as t1
where clinic_id <> 1 and 
      not exists (select 1
                  from mytable as t2
                  where t1.emp_id = t2.emp_id and t2.clinic_id = 1)

阻止版本:

  for(size_t i = 0; i < n; ++i)
  {
    for(size_t k = 0; k < n; ++k)
    {
      int r = a[i][k];
      for(size_t j = 0; j < n; ++j)
      {
        c[i][j] += r * b[k][j];
      }
    }
  }

我还有一个bijk版本和一个6循环bikj版本,但它们都得到了非阻塞版本的表现,我不知道为什么会发生这种情况。我遇到的每篇论文和教程似乎表明被阻止的版本应该明显更快。如果重要的话,我在Core i5上运行它。

1 个答案:

答案 0 :(得分:3)

仅尝试在一个维度中进行屏蔽,而不是在两个维度中进行屏蔽。

矩阵乘法穷尽地处理来自两个矩阵的元素。左矩阵上的每个行向量被重复处理,被带入右矩阵的连续列中。

如果矩阵都不适合缓存,一些数据总是会多次加载。

我们可以做的是分解操作,以便我们一次处理大约一个缓存大小的数据。我们希望缓存左操作数中的行向量,因为它会重复应用于多个列。但是我们应该只采取足够的列(一次)保持在缓存的限制范围内。例如,如果我们只能占用25%的列,则意味着我们必须将行向量传递四次。我们最终从内存中加载左矩阵四次,而右矩阵只加载一次。

(如果需要多次加载任何内容,它应该是左侧的行向量,因为它们在内存中是平坦的,这有利于突发加载。许多缓存架构可以执行从内存到邻近缓存的突发加载线条比随机访问加载更快。如果正确的矩阵以列主顺序存储,那就更好了:那么我们在平面数组之间进行交叉产品,这很好地预取到内存中。)

我们也不要忘记输出矩阵。输出矩阵也占用缓存中的空间。

我怀疑2D阻塞方法中的一个缺陷是输出矩阵的每个元素都依赖于两个输入:左边矩阵中的整个整行,右边矩阵中的整个列。如果以块的形式访问矩阵,则意味着多次访问每个目标元素以累积部分结果。

如果我们制作完整的行列点产品,我们不必多次访问c[i][j];一旦我们将列j放入行i,我们就完成了c[i][j]