我有一个运行线性代数实验的程序(使用C ++中的Armadillo库)。我需要多次运行该程序,因此要并行执行多个进程(取决于我的可用资源)。但是,即使仅并行运行两个实验,这两个过程也会减慢不成比例的数量-与顺序执行时相比,它们至少要慢10倍。
我设法将代码减少到出现问题的地方,而这似乎是我将矩阵乘以向量的地方。这是一个可重现的示例。这将无限运行,并且每隔1000次将矩阵乘以向量,就会打印出经过的秒数。
test.cpp
#include <iostream>
#include <armadillo>
#include <chrono>
using namespace std;
using namespace arma;
int main(int argc, char** argv) {
chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
mat A = randn(1500,1500);
vec y = randn(1500);
vec values;
long its = 0;
while (true) {
values = A*y;
// print time once in a while
if (++its % 1000 == 0) {
long secondsElapsed = chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - begin).count();
cout << "secs elapsed by iteration " << its << ": " << secondsElapsed << endl;
}
}
return 0;
}
我通过以下方式编译/链接:
$ g++ test.cpp -larmadillo -std=c++11
在终端中运行此程序的单个进程时,输出为:
secs elapsed by iteration 1000: 1
secs elapsed by iteration 2000: 2
secs elapsed by iteration 3000: 3
...
运行该程序的两个进程(在两个不同的终端中)时,进程1的输出为:
secs elapsed by iteration 1000: 24
secs elapsed by iteration 2000: 48
secs elapsed by iteration 3000: 74
...
和进程2的输出相似。当第二个进程也正在运行时,该进程的速度要慢24倍。
Linux上的“ top”实用程序显示两个进程中每个进程的cpu均为400%,因此总计为800%(我有4个核心的8个线程)。这两个过程分别占用了我的内存的0.3%。
为什么会发生这种情况,我该如何解决?我不确定自己如何调试,但想学习,所以如果有人可以在答案中提供一些调试此类情况的技巧,那就太好了!
平台详细信息:Linux 3.10.0-693.11.1.el7.x86_64,4核/ 8线程,8Gb RAM。 g ++(GCC)4.8.5 20150623(红帽4.8.5-36)。 ARMA版本:8.300.2(热带雪茄)。
答案 0 :(得分:2)
这里有几件事。
您没有进行优化。使用-O2
和-march=native
看起来Armadillo使用BLAS进行矢量矩阵乘法。秘密可能就在那。听起来好像该库已经设置为可以使用所有内核。您可能无法通过多处理压缩更多性能。好的...就像您说的那样,您有一个超线程四核x86_64,并且您希望使用8个逻辑内核而不是4个。但是请记住,超线程的工作原理是让2个线程同时使用处理器。由于通常有很多剩余的算术单元,因此它可以 提高性能。但是,如果BLAS内核在所有4个内核上都使用了所有SIMD单元(或者只是很好地利用了可用的算术单元),则同时运行的第二个实例本身将没有任何硬件,并且可能最终只能进入上下文切换的方式,这会弄乱硬件管道并导致缓存未命中。
我正在尝试一种可以测试此问题的方法。如果您可以访问具有不同数量物理内核的其他计算机,则可能会看到一种模式。一个理想的调试控件是能够告诉BLAS使用多少个内核,但是我不确定是否可用。
在这里最好了解CPU架构以及BLAS版本(如果您确实在使用BLAS)。
您没有提及您所做的任何分析。如果您尚未完成分析,那么现在将是一个美好的时光!如果此部分成为瓶颈,则可以从GPU加速中受益。或者,如果这只是瓶颈,但只是一点点,您可以通过管道传递程序的其他部分,以使该部分获得数据。或者,只要运行时间合理,您始终可以对当前速度感到满意。