使用mpirun来执行我的程序会大大降低性能

使用mpirun来执行我的程序会大大降低性能,mpi,intel,distributed-computing,openmpi,Mpi,Intel,Distributed Computing,Openmpi,我是MPI领域的新手。我使用“英特尔数学内核库”编写了我的程序,我想计算一个分块的矩阵乘法,这意味着我将大矩阵X沿列拆分为许多小矩阵,如下所示。我的矩阵很大,所以每次我只计算(N,M)x(M,N),我可以手动设置M XX^T = X_1X_1^T + X_2X_2^T + ... + X_nX_n^T 我首先将总线程数设置为16,M等于1024。然后我直接运行我的程序,如下所示。我检查我的cpu状态,发现cpu使用率是1600%,这是正常的 ./MMNET_MPI --block 1024 -

我是MPI领域的新手。我使用“英特尔数学内核库”编写了我的程序,我想计算一个分块的矩阵乘法,这意味着我将大矩阵X沿列拆分为许多小矩阵,如下所示。我的矩阵很大,所以每次我只计算(N,M)x(M,N),我可以手动设置M

XX^T = X_1X_1^T + X_2X_2^T + ... + X_nX_n^T
我首先将总线程数设置为16,M等于1024。然后我直接运行我的程序,如下所示。我检查我的cpu状态,发现cpu使用率是1600%,这是正常的

./MMNET_MPI --block 1024 --numThreads 16
但是,我尝试使用MPI运行我的程序,如下所示。然后我发现cpu使用率只有200-300%。奇怪的是,我将块数改为64,这样我的cpu使用率就可以提高1200%

mpirun -n 1 --bind-to none ./MMNET_MPI --block 1024 --numThreads 16
我不知道是什么问题。似乎
mpirun
执行了一些对我的程序有影响的默认设置。下面是我的矩阵乘法代码的一部分。命令
#pragma omp parallel for
旨在从压缩格式parallel中提取小的N×M矩阵。之后,我使用
clubs_ggemv
计算矩阵乘法

#include "MemoryUtils.h"
#include "Timer.h"
#include "omp.h"
#include <mpi.h>
#include <mkl.h>

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
  omp_set_num_threads(16);
  Timer timer;
  double start_time = timer.get_time();

  MPI_Init(&argc, &argv);

  int total_process;
  int id;
  MPI_Comm_size(MPI_COMM_WORLD, &total_process);
  MPI_Comm_rank(MPI_COMM_WORLD, &id);

  if (id == 0) {
    cout << "========== Testing MPI properties for MMNET ==========" << endl;
  }

  cout << "Initialize the random matrix ..." << endl;

  unsigned long N = 30000;
  unsigned long M = 500000;
  unsigned long snpsPerBlock = 1024;

  auto* matrix = ALIGN_ALLOCATE_DOUBLES(N*M);
  auto* vector = ALIGN_ALLOCATE_DOUBLES(N);
  auto* result = ALIGN_ALLOCATE_DOUBLES(M);
  auto *temp1 = ALIGN_ALLOCATE_DOUBLES(snpsPerBlock);
  memset(result, 0, sizeof(double) * M);

  cout << "Time for allocating is " << timer.update_time() << " sec" << endl;

  memset(matrix, 1.1234, sizeof(double) * N * M);
  memset(vector, 1.5678, sizeof(double) * N);
  // #pragma omp parallel for
  // for (unsigned long row = 0; row < N * M; row++) {
  //     matrix[row] = (double)rand() / RAND_MAX;
  // }

  // #pragma omp parallel for
  // for (unsigned long row = 0; row < N; row++) {
  //     vector[row] = (double)rand() / RAND_MAX;
  // }

  cout << "Time for generating data is " << timer.update_time() << " sec" << endl;

  cout << "Starting calculating..." << endl;

  for (unsigned long m0 = 0; m0 < M; m0 += snpsPerBlock) {
    uint64 snpsPerBLockCrop = std::min(M, m0 + snpsPerBlock) - m0;
    auto* snpBlock = matrix + m0 * N;

    MKL_INT row = N;
    MKL_INT col = snpsPerBLockCrop;
    double alpha = 1.0;
    MKL_INT lda = N;
    MKL_INT incx = 1;
    double beta = 0.0;
    MKL_INT incy = 1;
    cblas_dgemv(CblasColMajor, CblasTrans, row, col, alpha, snpBlock, lda, vector, incx, beta, temp1, incy);

    // compute XA
    double beta1 = 1.0;
    cblas_dgemv(CblasColMajor, CblasNoTrans, row, col, alpha, snpBlock, lda, temp1, incx, beta1, result, incy);
  }

  cout << "Time for computation is " << timer.update_time() << " sec" << endl;
  ALIGN_FREE(matrix);
  ALIGN_FREE(vector);
  ALIGN_FREE(result);
  ALIGN_FREE(temp1);
  return 0;
}

默认情况下,MKL实现了对要使用的线程数的一些智能动态选择。这由变量
MKL_DYNAMIC
控制,该变量默认设置为
TRUE
。MKL的文件说明:

如果您[sic]能够检测到MPI的存在,但无法确定是否已在线程安全模式下调用它(例如,使用MPICH 1.2.x无法检测到这一点),并且未将其默认值
TRUE
更改为
MKL\u DYNAMIC
,则“英特尔MKL”将运行一个线程

由于您调用
MPI\u Init()
而不是
MPI\u Init\u thread()
来初始化MPI,因此实际上是在要求单线程MPI级别(
MPI\u thread\u single
)。该库可以免费为您提供任何线程级别,并将保守地坚持使用
MPI\u THREAD\u SINGLE
。您可以在初始化后调用
MPI\u Query\u thread(&提供)
,查看输出值是否大于
MPI\u thread\u SINGLE

由于您将OpenMP和线程化MKL与MPI混合使用,因此您应该通过调用
MPI\u Init\u thread()
,告诉MPI在更高的线程支持级别进行初始化:

现在,现实情况是,即使MPI没有提供比
MPI\u thread\u SINGLE
更高级别的线程支持,但大多数不在主线程之外进行MPI调用的线程软件运行得非常好,也就是说,在大多数MPI实现中
MPI\u thread\u SINGLE
相当于
MPI\u thread\u漏斗状
。在这种情况下,将
MKL\u DYNAMIC
设置为
FALSE
应使MKL在没有
mpirun
的情况下运行:

mpirun -x MKL_DYNAMIC=FALSE ...
在任何情况下,由于程序接受线程数作为参数,因此只需调用
mkl\u set\u num\u threads()
omp\u set\u num\u threads()
,而不依赖于神奇的默认机制


编辑:启用全线程支持会产生后果-延迟增加,一些网络模块可能会拒绝工作,例如较旧的开放MPI版本中的InfiniBand模块,导致库悄悄地切换到较慢的传输,如TCP/IP。更好的请求
MPI\u线程\u漏斗状
并显式设置MKL和OpenMP线程的数量。

您的codeDoes中没有MPI正在运行
mpirun。。。性能统计。/MMNET\u MPI
对任何事情都有帮助吗<代码>--bind to none应该与正常运行它相同,但可能它实际上是在以某种方式设置CPU亲和力?在作业运行时,可以运行
taskset-p$(pidof MMNET\u MPI)
,以查询关联掩码,查看mpirun是否设置了除所有掩码以外的内容(
ffff
或其他内容)。或者如果它启动了你程序的多个副本?或者它通过OpenMP环境变量?(您使用的是什么操作系统?Linux?)如果您提供有关哪个MPI实现(开放MPI?)的信息,哪个版本,如何将
--numThreads
参数转换为MKL线程数,如何将可执行文件与MKL链接,等等,这将很有帮助。@ptb这只是我程序的一小部分。我只用一个进程运行这个程序。所以我相信这没关系。@PeterCordes我只是发现了一件有趣的事情。当我调用
mkl\u set\u num\u threads()
时,程序可以正常地充分利用cpu资源。
int provided;

MPI_Init_thread(NULL, NULL, MPI_THREAD_MULTIPLE, &provided);
// This ensures that MPI actually provides MPI_THREAD_MULTIPLE
if (provided < MPI_THREAD_MULTIPLE) {
  // Complain
}
Thread support: posix (MPI_THREAD_MULTIPLE: yes, OPAL support: yes, OMPI progress: no, ORTE progress: yes, Event lib: yes)
mpirun -x MKL_DYNAMIC=FALSE ...