为什么我的矩阵向量乘法没有缩放?

计算科学 并行计算 矩阵 拉帕克 布拉斯
2021-12-05 23:40:51

很抱歉,这篇文章很长,但我想首先包含我认为相关的所有内容。

我想要的是

我正在为密集矩阵实现 Krylov 子空间方法的并行版本。主要是GMRES、QMR和CG。我意识到(在分析之后)我的 DGEMV 例程是可悲的。所以我决定通过隔离它来集中精力。我曾尝试在 12 核机器上运行它,但以下结果适用于 4 核 Intel i3 笔记本电脑。趋势上没有太大区别。

我的KMP_AFFINITY=VERBOSE输出在这里可用。

我写了一个小代码:

size_N = 15000
A = randomly_generated_dense_matrix(size_N,size_N); %Condition Number is not bad
b = randomly_generated_dense_vector(size_N);
for it=1:n_times %n_times I kept at 50 
 x = Matrix_Vector_Multi(A,b);
end

我相信这模拟了 CG 50 次迭代的行为。

我试过的:

翻译

我最初是用 Fortran 编写代码的。我将它翻译成 C、MATLAB 和 Python (Numpy)。不用说,MATLAB 和 Python 太可怕了。令人惊讶的是,对于上述值,C 比 FORTRAN 好一两秒。始终如一。

剖析

我分析了要运行的代码,它运行了46.075几秒钟。这是MKL_DYNAMIC 设置为FALSE并使用所有内核的时候。如果我将 MKL_DYNAMIC 用作 true,那么在任何给定时间点,只有(大约)一半的内核在使用。以下是一些细节:

Address Line    Assembly                CPU Time

0x5cb51c        mulpd %xmm9, %xmm14     36.591s

最耗时的过程似乎是:

Call Stack                          LAX16_N4_Loop_M16gas_1
CPU Time by Utilization             157.926s
CPU Time:Total by Utilization       94.1%
Overhead Time                       0us
Overhead Time:Total                 0.0%    
Module                              libmkl_mc3.so   

这里有几张图片:在此处输入图像描述 在此处输入图像描述

结论:

我是一个真正的分析初学者,但我意识到速度仍然不好。顺序(1 个核心)代码在53 秒内完成。那是不到 1.1 的加速!

真正的问题:我应该怎么做才能提高我的加速?

我认为可能有帮助但我不能确定的东西:

  • Pthreads 实现
  • MPI (ScaLapack) 实现
  • 手动调优(我不​​知道怎么做。如果你建议这个,请推荐一个资源)

如果有人需要更多(尤其是关于内存)的详细信息,请告诉我应该运行什么以及如何运行。我以前从未记忆过。

3个回答

您的矩阵大小为 15,000 x 15,000,因此矩阵中有 225M 个元素。这使得大约 2GB 的内存。这比处理器的缓存大小要大得多,因此在每次矩阵乘法中必须从主内存中完全加载它,从而进行大约 100GB 的数据传输,再加上源和目标向量所需的数据。

根据英特尔规格,i3 的最大内存带宽约为 21 GB/s,但如果您浏览网络,您会发现其中最多有一半是真正可用的。因此,至少,您希望您的基准测试持续 10 秒,而您实际测量的 45 秒与该标记相差不远。

同时,你也在做大约 100 亿次浮点乘法和加法。考虑到组合的 10 个时钟周期和 3 GHz 的时钟速率,您将在约 30 秒时出来。当然,如果缓存很聪明,它们可以与推测性内存负载同时运行。

总而言之,我会说你并没有离题太远。你会期待什么?

你是如何做矩阵向量乘法的?手动双循环?还是打电话给 BLAS?如果您使用的是 MKL,我强烈建议您使用线程版本的 BLAS 例程。

出于好奇,您可能还想编译您自己的ATLAS调整版本,看看它如何解决您的问题。

更新

根据下面评论中的讨论,事实证明您的英特尔酷睿 i3-330M 只有两个“真正的”核心。两个缺失的内核用超线程模拟。由于在超线程内核中,内存总线和浮点单元都是共享的,因此如果两者中的任何一个是限制因素,您将不会获得任何加速。事实上,使用四个核心可能甚至会减慢速度。

您在“仅”两个核心上得到什么样的结果?

我的印象是,就内存访问时间、缓存行使用和 TLB 未命中而言,行优先排序对于这个问题是最佳的。我猜你的 FORTRAN 版本使用了列优先排序,这可以解释为什么它总是比 C 版本慢。

如前所述,您的内存带宽在这里受到限制。可能出错的是向量 $b$ 没有保存在缓存中。您可以测试 size_N = 15000 与 size_N = 5000 时是否观察到相同(有效)的内存带宽。如果这样做,则很有可能代码已经是最佳的,并且系统的内存带宽很简单没那么好。b isn't kept in the cache. You could test whether you observe the same (effective) memory bandwidth for size_N = 15000 than for size_N = 5000. If you do, there is a pretty good chance that the code is already optimal, and that the memory bandwidth of your system is simply not that great.

如果您只是在一个循环中总结矩阵的所有元素而不是矩阵向量乘法,您也可以测试速度。(您可能希望将循环展开 4 倍,因为加法的非关联性可能会阻止编译器为您进行此优化。)