# 3.6 检测MPI的并行环境

**NOTE**:*此示例代码可以在* <https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-06> *中找到，包含一个C++和一个C的示例。该示例在CMake 3.9版(或更高版本)中是有效的，并且已经在GNU/Linux、macOS和Windows上进行过测试。*<https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-06> *中也有一个适用于CMake 3.5的C示例。*

消息传递接口(Message Passing Interface, MPI)，可以作为OpenMP(共享内存并行方式)的补充，它也是分布式系统上并行程序的实际标准。尽管，最新的MPI实现也允许共享内存并行，但高性能计算中的一种典型方法就是，在计算节点上OpenMP与MPI结合使用。MPI标准的实施包括:

1. 运行时库
2. 头文件和Fortran 90模块
3. 编译器的包装器，用来调用编译器，使用额外的参数来构建MPI库，以处理目录和库。通常，包装器`mpic++/mpiCC/mpicxx`用于C++，`mpicc`用于C，`mpifort`用于Fortran。
4. 启动MPI：应该启动程序，以编译代码的并行执行。它的名称依赖于实现，可以使用这几个命令启动：`mpirun`、`mpiexec`或`orterun`。

本示例，将展示如何在系统上找到合适的MPI实现，从而编译一个简单的“Hello, World”MPI例程。

## 准备工作

示例代码(`hello-mpi.cpp`，可从<http://www.mpitutorial.com> 下载)将在本示例中进行编译，它将初始化MPI库，让每个进程打印其名称:

```cpp
#include <iostream>

#include <mpi.h>

int main(int argc, char **argv)
{
  // Initialize the MPI environment. The two arguments to MPI Init are not
  // currently used by MPI implementations, but are there in case future
  // implementations might need the arguments.
  MPI_Init(NULL, NULL);

  // Get the number of processes
  int world_size;
  MPI_Comm_size(MPI_COMM_WORLD, &world_size);

  // Get the rank of the process
  int world_rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

  // Get the name of the processor
  char processor_name[MPI_MAX_PROCESSOR_NAME];
  int name_len;
  MPI_Get_processor_name(processor_name, &name_len);

  // Print off a hello world message
  std::cout << "Hello world from processor " << processor_name << ", rank "
            << world_rank << " out of " << world_size << " processors" << std::endl;

  // Finalize the MPI environment. No more MPI calls can be made after this
  MPI_Finalize();
}
```

## 具体实施

这个示例中，我们先查找MPI实现：库、头文件、编译器包装器和启动器。为此，我们将用到`FindMPI.cmake`标准CMake模块:

1. 首先，定义了CMake最低版本、项目名称、支持的语言和语言标准:

   ```
   cmake_minimum_required(VERSION 3.9 FATAL_ERROR)

   project(recipe-06 LANGUAGES CXX)

   set(CMAKE_CXX_STANDARD 11)
   set(CMAKE_CXX_EXTENSIONS OFF)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   ```
2. 然后，调用`find_package`来定位MPI:

   ```
   find_package(MPI REQUIRED)
   ```
3. 与前面的配置类似，定义了可执行文件的的名称和相关源码，并链接到目标:

   ```
   add_executable(hello-mpi hello-mpi.cpp)

   target_link_libraries(hello-mpi
     PUBLIC
          MPI::MPI_CXX
     )
   ```
4. 配置和构建可执行文件:

   ```
   $ mkdir -p build
   $ cd build
   $ cmake .. # -D CMAKE_CXX_COMPILER=mpicxx C++例子中可加，加与不加对于构建结果没有影响╭(╯^╰)╮

   -- ...
   -- Found MPI_CXX: /usr/lib/openmpi/libmpi_cxx.so (found version "3.1")
   -- Found MPI: TRUE (found version "3.1")
   -- ...

   $ cmake --build .
   ```
5. 为了并行执行这个程序，我们使用`mpirun`启动器(本例中，启动了两个任务):

   ```
   $ mpirun -np 2 ./hello-mpi

   Hello world from processor larry, rank 1 out of 2 processors
   Hello world from processor larry, rank 0 out of 2 processors
   ```

## 工作原理

请记住，编译包装器是对MPI库编译器的封装。底层实现中，将会调用相同的编译器，并使用额外的参数(如成功构建并行程序所需的头文件包含路径和库)来扩充它。

编译和链接源文件时，包装器用了哪些标志？我们可以使用`--showme`选项来查看。要找出编译器的标志，我们可以这样使用:

```
$ mpicxx --showme:compile

-pthread
```

为了找出链接器标志，我们可以这样:

```
$ mpicxx --showme:link

-pthread -Wl,-rpath -Wl,/usr/lib/openmpi -Wl,--enable-new-dtags -L/usr/lib/openmpi -lmpi_cxx -lmpi
```

与之前的OpenMP配置类似，我们发现到MPI的链接非常简单，这要归功于`FindMPI`模块提供的目标:

正如在前面的配方中所讨论的，对于CMake版本低于3.9，需要更多的工作量:

```
add_executable(hello-mpi hello-mpi.c)

target_compile_options(hello-mpi
  PUBLIC
      ${MPI_CXX_COMPILE_FLAGS}
  )

target_include_directories(hello-mpi
  PUBLIC
      ${MPI_CXX_INCLUDE_PATH}
  )

target_link_libraries(hello-mpi
  PUBLIC
      ${MPI_CXX_LIBRARIES}
  )
```

本示例中，我们讨论了C++项目。其中的参数和方法对于C或Fortran项目同样有效。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/3.0-chinese/3.6-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
