运行 与 OpenMPI 的专有并行

Run Eigen Parallel with OpenMPI

我是 Eigen 的新手,正在编写一些简单的代码来测试它的性能。我使用的是 M1 Pro 芯片 的 MacBook Pro(不知道是不是 ARM 架构导致的问题)。代码是一个简单的拉普拉斯方程求解器

#include <iostream>
#include "mpi.h"
#include "Eigen/Dense"
#include <chrono>
 
using namespace Eigen;
using namespace std;

const size_t num = 1000UL;

MatrixXd initilize(){
    MatrixXd u = MatrixXd::Zero(num, num);
    u(seq(1, fix<num-2>), seq(1, fix<num-2>)).setConstant(10);
    return u;
}

void laplace(MatrixXd &u){
    setNbThreads(8);
    MatrixXd u_old = u;

    u(seq(1,last-1),seq(1,last-1)) =
    ((  u_old(seq(0,last-2,fix<1>),seq(1,last-1,fix<1>)) + u_old(seq(2,last,fix<1>),seq(1,last-1,fix<1>)) +
        u_old(seq(1,last-1,fix<1>),seq(0,last-2,fix<1>)) + u_old(seq(1,last-1,fix<1>),seq(2,last,fix<1>)) )*4.0 +
        u_old(seq(0,last-2,fix<1>),seq(0,last-2,fix<1>)) + u_old(seq(0,last-2,fix<1>),seq(2,last,fix<1>)) +
        u_old(seq(2,last,fix<1>),seq(0,last-2,fix<1>))   + u_old(seq(2,last,fix<1>),seq(2,last,fix<1>)) ) /20.0;
}


int main(int argc, const char * argv[]) {
    initParallel();
    setNbThreads(0);
    cout << nbThreads() << endl;
    MatrixXd u = initilize();
    
    auto start  = std::chrono::high_resolution_clock::now();
    
    for (auto i=0UL; i<100; i++) {
        laplace(u);
    }
    
    auto stop  = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
    
    // cout << u(seq(0, fix<10>), seq(0, fix<10>)) << endl;
    cout << "Execution time (ms): " << duration.count() << endl;
    return 0;
}

编译 gcc 并启用 OpenMPI

james@MBP14 tests % g++-11 -fopenmp  -O3 -I/usr/local/include -I/opt/homebrew/Cellar/open-mpi/4.1.3/include -o test4 test.cpp

直接运行二进制文件

james@MBP14 tests % ./test4
8
Execution time (ms): 273

运行 与 mpirun 并指定 8 个线程

james@MBP14 tests % mpirun -np 8 test4
8
8
8
8
8
8
8
8
Execution time (ms): 348
Execution time (ms): 347
Execution time (ms): 353
Execution time (ms): 356
Execution time (ms): 350
Execution time (ms): 353
Execution time (ms): 357
Execution time (ms): 355

所以显然矩阵运算不是 运行 并行的,相反,每个线程都 运行 是相同的代码副本。

应该怎么做才能解决这个问题?我对使用 OpenMPI 有什么误解吗?

您将 OpenMPI 与 OpenMP 混淆了。

  • gcc 标志 -fopenmp 启用 OpenMP。这是通过在代码中使用特殊的 #pragma omp 语句来并行化应用程序的一种方法。并行化发生在 单个 CPU(或者,准确地说,计算节点,如果计算节点有多个 CPU)。这允许使用 CPU 的所有核心。 OpenMP 不能用于在多个计算节点上并行化应用程序。
  • 另一方面,MPI (where OpenMPI 是一个特定的实现)可用于在 多个计算节点 上并行化代码(即,粗略地说,在多台计算机上连接)。它还可用于在单台计算机的多个内核上并行化某些代码。所以 MPI 更通用,但也更难使用。

要使用 MPI,您需要调用“特殊”函数并自己完成分发数据的艰苦工作。如果不这样做,使用 mpirun 调用应用程序只会创建几个相同的进程(不是线程!),它们执行完全相同的计算。您还没有并行化您的应用程序,您只是执行了 8 次。

没有启用 MPI 的编译器标志。 MPI 没有内置到任何编译器中。相反,MPI 是一种标准,而 OpenMPI 是一个实现该标准的特定库。您应该阅读有关 MPI 和 OpenMPI 的教程或书籍(例如 google 出现 this one)。

注意:通常,MPI 库(例如 OpenMPI)附带 executables/scripts(例如 mpicc),其行为类似于编译器。但它们只是编译器的薄包装,例如 gcc。这些包装器用于自动告诉实际编译器包含目录和库 link 。但同样,编译器本身对 MPI 一无所知。