【问题标题】:MPI performance loss by increasing number of processes进程数量增加导致 MPI 性能损失
【发布时间】:2020-11-18 12:46:27
【问题描述】:

我正在使用带有 MPI 的 c++ 来执行一些线性代数计算,例如特征值分解。这些计算对于每个进程来说完全是本地的,所以我认为单进程性能应该不受我运行的进程总数的影响,只要有足够的计算资源。

然而,事实证明,随着进程总数的增加,每个进程的性能都会下降。在一个由 2 个 Intel Xeon Gold 6132 CPU(总共 28 个物理内核或 56 个线程)组成的节点上,我的测试发现,单个进程的 2000×2000 对称矩阵的特征分解大约需要 1.1 秒, 4 个独立进程(使用 mpirun -np 4 ./test)为 1.3 秒,12 个进程为 1.8 秒。

我想知道,这是 MPI 的预期行为,还是我错过了一些绑定选项?我试过“mpirun -np 12 --bind-to core:12 ./test”,但没有帮助。我正在使用 Armadillo 库,它与 Intel MKL 链接。环境变量MKL_NUM_THREADS设置为1,附源码。

#include <mpi.h>
#include <armadillo>
#include <chrono>
#include <sstream>

using namespace arma;
using iclock = std::chrono::high_resolution_clock;

int main(int, char**argv) {

    ////////////////////////////////////////////////////
    //              MPI Initialization
    ////////////////////////////////////////////////////
    int id, nprocs;
    MPI_Init(nullptr, nullptr);
    MPI_Comm_rank(MPI_COMM_WORLD, &id);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);

    ////////////////////////////////////////////////////
    //              parse arguments
    ////////////////////////////////////////////////////
    int sz = 0, nt = 0;
    std::stringstream ss; 

    if (id == 0) {
        ss << argv[1];
        ss >> sz; 
        ss.clear();
        ss.str("");

        ss << argv[2];
        ss >> nt; 
        ss.clear();
        ss.str("");
    }   

    MPI_Bcast(&sz, 1, MPI_INT, 0, MPI_COMM_WORLD);
    MPI_Bcast(&nt, 1, MPI_INT, 0, MPI_COMM_WORLD);

    ////////////////////////////////////////////////////
    //                test and timing
    ////////////////////////////////////////////////////
    mat a = randu(sz, sz);
    a += a.t();

    mat evec(sz, sz);
    vec eval(sz);

    iclock::time_point start = iclock::now();

    for (int i = 0; i != nt; ++i) {
        //evec = a*a;
        eig_sym(eval, evec, a); // <-------here
    }   

    std::chrono::duration<double> dur = iclock::now() - start;

    double t = dur.count() / nt;

    ////////////////////////////////////////////////////
    //               collect timing
    ////////////////////////////////////////////////////
    vec durs(nprocs);
    MPI_Gather(&t, 1, MPI_DOUBLE, durs.memptr(), 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

    if (id == 0) {
        std::cout << "average time elapsed of each proc:" << std::endl;
        durs.print();
    }

    MPI_Finalize();

    return 0;
}

【问题讨论】:

  • 您应该提供运行测试时使用的sz 的值。计算资源并不是多个进程共享的唯一资源。最后一级缓存和内存带宽也受到限制。
  • 基于@HristoIliev 所说的,犰狳可能会使用绑定到 LAPACK 和/或 BLAS 的实际线性代数,这将使用有关机器缓存大小的知识来提高内存吞吐量。并行运行多个进程意味着您有更多的缓存争用和更低的整体吞吐量。

标签: c++ performance mpi armadillo intel-mkl


【解决方案1】:

这是预期的行为。要获得 MPI 的性能,您必须执行数据分解(负载平衡)、通信优化(阻塞与非阻塞)等。

据我从问题中了解到,12 个进程在 12 个2000X2000 矩阵上进行计算,平均时间为 1.8 秒,而单个进程执行计算的平均时间仅为 1.2 秒。

是的,对于上述场景,MPI 性能不会比单个进程更好,并且必须更高,原因如下(其中一些在 cmets 中被 Hristo Iliev 提及):

  1. 由 MPI 引起的开销
  2. MPI 中最慢的进程所花费的时间
  3. 内存带宽(每个进程需要访问2000*2000的矩阵,可能会导致争用)。
  4. 缓存(更高级别的缓存在处理器之间共享,多个进程频繁访问缓存等可能会影响整体应用程序性能)

此外,性能改进(加速)将基于应用程序的并行部分,由于您的应用程序中没有并行部分,因此您不会观察到并行化的任何好处。

另外,如果一个 2000X2000 的矩阵分布在 12 个进程中,我们不能保证 MPI 的性能会优于单个进程。这将取决于实现。

【讨论】:

    【解决方案2】:

    您是用总运行时间除以进程数,还是考虑了调度开销?运行时中的工作调度程序将需要一些开销处理时间,该时间随着进程数与机器上内核数的比率而增加。您可能需要降低并行粒度(每个处理器的进程数)以优化速度。这是正常情况下的预期行为。

    但是,您设置的条件不正常。 设置 MKL_NUM_THREADS=1 可防止产生超过 1 个线程! 删除设置MLK_NUM_THREADS的行,系统会为你处理。

    【讨论】:

    • 对不起,也许我在帖子中不清楚......在附加的代码中,我实际上是在每个过程中对角化一个矩阵(通过几次并取平均值),我只有时间对角化,而不是整个程序。我认为这对于每个进程应该是完全独立的,因为根本没有通信。但是我刚刚发现,在一台有 28 个物理内核的机器上,同时对角化 12 个矩阵比只对角化一个矩阵要慢 70%,即使我坚持每个对角化都是由一个线程执行的。这仍然是预期的吗?
    • @jinzx10 你说 MKL_NUM_THREADS 设置为 1?为什么不直接使用默认值?
    • 我同意@jinzx10,请尝试使用 MKL_NUM_THREADS=28 并重新运行代码。
    猜你喜欢
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2020-04-02
    相关资源
    最近更新 更多