给定任何2个matrics a和b(它们没有特殊属性)我们是否有更好的计算乘法的方法:?
for(i=0; i<r1; ++i)
for(j=0; j<c2; ++j)
for(k=0; k<c1; ++k)
{
mult[i][j]+=a[i][k]*b[k][j];
}
答案 0 :(得分:7)
如果你对它们理论上是否存在感到好奇,那么是的。例如,Strassen算法(参见https://en.wikipedia.org/wiki/Strassen_algorithm)。它甚至不是我们所知道的最快的。至于我现在最关心的是Coppersmith-Winograd算法(见https://en.wikipedia.org/wiki/Coppersmith%E2%80%93Winograd_algorithm),它类似于O(n^{2.37})
(Strassen的时间复杂度类似{{1} }}
但实际上它们比你编写的那个更难实现,并且它们在O(n^{2.8})
下隐藏了相当大的时间常数,所以你写的O()
算法在{O(n^3)
的低值时更好。 1}}并且更容易实现。
还有一个Strassen的假设声称,对于每个n
,都有一个算法将两个矩阵与时间复杂度eps > 0
相乘。但是你可能已经注意到它现在只是一个假设。
答案 1 :(得分:3)
作为一种非常简单的解决方案,您可以在乘法之前转置第二个矩阵,这样您的代码将获得更少的处理器缓存未命中。复杂性将是相同的,但它可能会稍微改善时间常数。
答案 2 :(得分:2)
这是世界上许多聪明人在你面前解决的问题。不要折磨自己并使用BLAS?GEMM。
答案 3 :(得分:1)
这是一个很好的问题,应该比“使用图书馆”更完整的答案。
当然,如果你想做得好,你可能不应该尝试自己写。但如果这个问题是关于学习如何更快地进行矩阵乘法,那么这是一个完整的答案。
实际上,您展示的代码会过多地写入内存。如果内部循环在标量变量中添加点积,那么只在末尾写入,代码会更快。大多数编译器都不够聪明,无法理解这一点。
双点= 0; for(k = 0; k
这也提高了多核性能,因为如果使用多个内核,则必须共享内存带宽。 如果您使用的是行数组,请将表示形式切换为单个内存块。
如上所述,您可以进行转置,因此矩阵遍历都是按顺序进行的。内存被设计为按顺序有效读取,但是你的b [k] [j]跳跃,所以这大约快3倍,因为大小变大(大约1000x1000,初始转置的成本可以忽略不计)
当矩阵变得足够大时,Strassen和Coppersmith-Winograd是更快的乘法方式,从根本上改变了规则,但是他们通过巧妙地重新排列术语来实现相同的理论结果,并且复杂度更低。在实践中,他们改变了答案,因为舍入误差是不同的,对于大型矩阵,这些算法产生的答案可能比蛮力乘法更糟糕。
如果您有一台真正的并行计算机,您可以将矩阵复制到多个CPU并让它们并行处理答案。
您可以将代码放到视频卡上,并使用那些具有更多内存带宽的并行CPU。这可能是在您的计算机上获得真正加速的最有效方法(假设您有一个显卡)。见CUDA或Vulkan。
基本问题是多核对矩阵乘法没有多大帮助,因为你受内存带宽的限制。这就是为什么在显卡上做这么好,因为那里的带宽要高得多。
答案 4 :(得分:0)
您可以通过将乘法除以它们来使用多个线程。因此,将第一个矩阵的第一个维度的行/列或者最后一个维度的行/列分成多个任务,这些任务等于处理器中的核心数。如果这些不可分割,则某些核心必须进行额外的循环。但无论如何,这个想法是给多个核心增加乘法,例如4个部分中的第一个矩阵(我有4个核心),用4个任务进行乘法运算,然后重新组合(这是不必要的,因为核心可以处理相同的数据)。