前面写了一个关于《一个完整的cmake+clang+llvm编译链接hello-world过程》,感觉写这个的时候有点多余,因为大多数都是依葫葫芦画瓢,不过刚刚碰到一些MPI的问题,在网上找了一圈,发现太多关于MPI的误人子弟的贴子(我想主要原因可能是因为这些帖子太老了,不适用,软件都已经更新了好几代了),所以在这里写个简单的hello-world澄清一下。
比如MPI入门级的大量的报错, Linking Error: undefined reference to `MPI_Init' 网上多数贴子的理由五花八门,比如说要用mpi++而不是mpicc啦,gcc语法不对啦,其实最根本的原因是因为没有找到链接MPI的库文件,下面我们详细说明,知其然,知其所以然。
源码mpich学习的源码部分可参考https://github.com/wesleykendall/mpitutorial (克隆命令:git clone https://github.com/wesleykendall/mpitutorial.git) 我取其中最简单那个做个示范,我安装的是OpenMPI,这主要是因为我用到的项目大多依赖OpenMPI而不是MPICH。
目录结构是这样的 ~/devc/mpitutorial/tutorials/mpi-hello-world mpi-hello-world |__linux.toolchain.cmake |__CMakeLists.txt |__src |__CMakeLists.txt |__mpi_hello_world.c |__build |_(在此执行cmake+make操作)
内容如下,
mpi-hello-world / linux.toolchain.cmake
cmake_minimum_required( VERSION 2.6.3 )
set(CMAKE_SYSTEM_NAME Linux )
SET (CMAKE_C_COMPILER "/usr/bin/clang")
SET (CMAKE_C_FLAGS "-Wall -std=c99")
SET (CMAKE_C_FLAGS_DEBUG "-g")
SET (CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG")
SET (CMAKE_C_FLAGS_RELEASE "-O4 -DNDEBUG")
SET (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")
SET (CMAKE_CXX_COMPILER "/usr/bin/clang++")
SET (CMAKE_CXX_FLAGS "-Wall")
SET (CMAKE_CXX_FLAGS_DEBUG "-g")
SET (CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
SET (CMAKE_AR "/usr/bin/llvm-ar")
SET (CMAKE_LINKER "/usr/bin/llvm-ld")
SET (CMAKE_NM "/usr/bin/llvm-nm")
SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump")
SET (CMAKE_RANLIB "/usr/bin/llvm-ranlib")
mpi-hello-world / CMakeLists.txt
cmake_minimum_required(VERSION 3.7.1)
project(hello)
add_subdirectory(src)
mpi-hello-world / src / CMakeLists.txt
cmake_minimum_required(VERSION 3.7.1)
project(hello-world)
find_package(LLVM REQUIRED CONFIG)
find_package(MPI REQUIRED)
set(SOURCE_FILES mpi_hello_world.c)
message(STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR})
message(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Using LLVM_INCLUDE_DIRS: ${LLVM_INCLUDE_DIRS}")
message(STATUS "Using LLVM_DEFINITIONS: ${LLVM_DEFINITIONS}")
message(STATUS "Using MPI_INCLUDE_PATH: ${MPI_INCLUDE_PATH}")
message(STATUS "Using MPI_C_LIBRARIES: ${MPI_C_LIBRARIES}")
#/usr/lib/x86_64-linux-gnu/openmpi/include
include_directories(SYSTEM ${MPI_INCLUDE_PATH})
include_directories(${LLVM_INCLUDE_DIRS} )
add_definitions(${LLVM_DEFINITIONS})
add_executable(hello ${SOURCE_FILES})
llvm_map_components_to_libnames(llvm_libs support core irreader)
target_link_libraries(hello ${llvm_libs} ${MPI_C_LIBRARIES})
mpi-hello-world / src / mpi-hello-world.c
// Author: Wes Kendall
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
// either provide a link to www.mpitutorial.com or keep this header intact.
//
// An intro MPI hello world program that uses MPI_Init, MPI_Comm_size,
// MPI_Comm_rank, MPI_Finalize, and MPI_Get_processor_name.
//
#include
#include
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
printf("Hello world from processor %s, rank %d out of %d processors\n",
processor_name, world_rank, world_size);
// Finalize the MPI environment. No more MPI calls can be made after this
MPI_Finalize();
}
编译源码并运行
注意我在CMakeLists.txt中加了相当多的调试信息,切换到build目录下后,执行,
$ cmake -DCMAKE_TOOLCHAIN_FILE=../linux.toolchain.cmake ..
$ make VERBOSE=1
就可得到详细的运行信息,如下
$ cmake -DCMAKE_TOOLCHAIN_FILE=../linux.toolchain.cmake ..
-- The C compiler identification is Clang 6.0.0
-- The CXX compiler identification is Clang 6.0.0
-- Check for working C compiler: /usr/bin/clang
-- Check for working C compiler: /usr/bin/clang -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/clang++
-- Check for working CXX compiler: /usr/bin/clang++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found MPI_C: /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi.so (found version "3.1")
-- Found MPI_CXX: /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi_cxx.so (found version "3.1")
-- Found MPI: TRUE (found version "3.1")
-- This is BINARY dir /home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build/src
-- This is SOURCE dir /home/user01/devc/mpitutorial/tutorials/mpi-hello-world/src
-- Found LLVM 6.0.0
-- Using LLVMConfig.cmake in: /usr/lib/llvm-6.0/cmake
-- Using LLVM_INCLUDE_DIRS: /usr/lib/llvm-6.0/include
-- Using LLVM_DEFINITIONS: -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
-- Using MPI_INCLUDE_PATH: /usr/lib/x86_64-linux-gnu/openmpi/include/openmpi;
/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi/opal/mca/event/libevent2022/libevent;
/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi/opal/mca/event/libevent2022/libevent/include;
/usr/lib/x86_64-linux-gnu/openmpi/include
-- Using MPI_C_LIBRARIES: /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build
$ make VERBOSE=1
/usr/bin/cmake -H/home/user01/devc/mpitutorial/tutorials/mpi-hello-world
-B/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build
--check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start
/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build/CMakeFiles
/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
......
Scanning dependencies of target hello
......
make[2]: 离开目录“/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build”
[100%] Built target hello
make[1]: 离开目录“/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build”
/usr/bin/cmake -E cmake_progress_start
/home/user01/devc/mpitutorial/tutorials/mpi-hello-world/build/CMakeFiles 0
最后生成的可执行文件在 mpi-hello-world / build / src / hello。
关于MPI宏定义: MPI_C_LIBRARIES和MPI_C_INCLUDE_PATHLLVM相关的宏定义都在文件LLVMConfig.cmake中,一般情况下默认的路径如下所示 /usr/lib/llvm-6.0/cmake/LLVMConfig.cmake
CMake中的module findMPI.cmake在如下路径, /usr/share/cmake-3.10/Modules /usr/share/cmake-3.13/Modules 如果你对findMPI感兴趣,可以详细阅读一下源码,其中有定义MPI_C_LIBRARIES和MPI_C_INCLUDE_PATH 注意:MPI_C_INCLUDE_PATH和MPI_INCLUDE_PATH的效力是一样的,这一点cmake官方文档中有详细说明,可参考:https://cmake.org/cmake/help/v3.13/module/FindMPI.html 链接库在这里target_link_libraries(hello ${llvm_libs} ${MPI_C_LIBRARIES})从调试输出部分也可以看到,MPI_C_LIBRARIES相当于链接到了这个库 /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi.so
总结总结一下MPI编程的基本步骤
step.1: 对于 MPI ,首先你必须先找到MPI find_package(MPI) #必需的
step.2: 然后将其添加的头文件到您的搜索路径 include_directories(SYSTEM $ {} MPI_INCLUDE_PATH)
step.3:和最后链接程序 target_link_libraries(target ${MPI_C_LIBRARIES})
参考资料
https://cmake.org/cmake/help/v3.13/module/FindMPI.htmlhttps://github.com/wesleykendall/mpitutorial