ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 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库,让每个进程打印其名称: ```c++ #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 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: ```cmake find_package(MPI REQUIRED) ``` 3. 与前面的配置类似,定义了可执行文件的的名称和相关源码,并链接到目标: ```cmake add_executable(hello-mpi hello-mpi.cpp) target_link_libraries(hello-mpi PUBLIC MPI::MPI_CXX ) ``` 4. 配置和构建可执行文件: ```shell $ 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`启动器(本例中,启动了两个任务): ```shell $ 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`选项来查看。要找出编译器的标志,我们可以这样使用: ```shell $ mpicxx --showme:compile -pthread ``` 为了找出链接器标志,我们可以这样: ```shell $ 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,需要更多的工作量: ```cmake 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项目同样有效。