本文將介紹 Windows 下,使用 CLion 和 WSL 配置 MPI 執行及除錯環境的方法。
0. 前提
閱讀本文前,請確保:
- Windows 下已啟用 WSL2,並安裝了任一 Linux 發行版
1. WSL環境配置
(1) 配置編譯環境
sudo apt-get update
sudo apt-get install build-essential cmake gdb
(2) 配置MPI
MPI 環境可選擇透過包管理器配置,也可自行編譯原始碼配置。簡便起見,本文采用包管理器進行配置,選用的 MPI 實現為 MPICH,安裝命令為:
# Debian、Ubuntu等發行版使用如下命令安裝:
sudo apt-get install mpich
如果安裝成功,執行命令 mpichversion
,將輸出:
之後,嘗試執行測試程式,將以下程式碼儲存到檔案 mpi_hello.c 中:
/* File:
* mpi_hello.c
*
* Purpose:
* A "hello,world" program that uses MPI
*
* Compile:
* mpicc -g -Wall -std=C99 -o mpi_hello mpi_hello.c
* Usage:
* mpiexec -n<number of processes> ./mpi_hello
*
* Input:
* None
* Output:
* A greeting from each process
*
* Algorithm:
* Each process sends a message to process 0, which prints
* the messages it has received, as well as its own message.
*
* IPP: Section 3.1 (pp. 84 and ff.)
*/
#include <stdio.h>
#include <string.h> /* For strlen */
#include <mpi.h> /* For MPI functions, etc */
const int MAX_STRING = 100;
int main(void) {
char greeting[MAX_STRING]; /* String storing message */
int comm_sz; /* Number of processes */
int my_rank; /* My process rank */
/* Start up MPI */
MPI_Init(NULL, NULL);
/* Get the number of processes */
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
/* Get my rank among all the processes */
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
if (my_rank != 0) {
/* Create message */
sprintf(greeting, "Greetings from process %d of %d!",
my_rank, comm_sz);
/* Send message to process 0 */
MPI_Send(greeting, strlen(greeting)+1, MPI_CHAR, 0, 0,
MPI_COMM_WORLD);
} else {
/* Print my message */
printf("Greetings from process %d of %d!\n", my_rank, comm_sz);
for (int q = 1; q < comm_sz; q++) {
/* Receive message from process q */
MPI_Recv(greeting, MAX_STRING, MPI_CHAR, q,
0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/* Print message from process q */
printf("%s\n", greeting);
}
}
/* Shut down MPI */
MPI_Finalize();
return 0;
} /* main */
編譯並執行:
# 編譯
mpicc -g -Wall -std=C99 -o mpi_hello mpi_hello.c
# 執行,4表示程序數
mpiexec -n 4 ./mpi_hello
輸出應為:
Greetings from process 0 of 4!
Greetings from process 1 of 4!
Greetings from process 2 of 4!
Greetings from process 3 of 4!
2. CLion執行環境配置
(1) 新建專案
CLion 新建一個專案 mpi,將 main.c 或 main.cpp 內容修改為:
/* File:
* mpi_hello.c
*
* Purpose:
* A "hello,world" program that uses MPI
*
* Compile:
* mpicc -g -Wall -std=C99 -o mpi_hello mpi_hello.c
* Usage:
* mpiexec -n<number of processes> ./mpi_hello
*
* Input:
* None
* Output:
* A greeting from each process
*
* Algorithm:
* Each process sends a message to process 0, which prints
* the messages it has received, as well as its own message.
*
* IPP: Section 3.1 (pp. 84 and ff.)
*/
#include <stdio.h>
#include <string.h> /* For strlen */
#include <mpi.h> /* For MPI functions, etc */
const int MAX_STRING = 100;
int main(void) {
char greeting[MAX_STRING]; /* String storing message */
int comm_sz; /* Number of processes */
int my_rank; /* My process rank */
/* Start up MPI */
MPI_Init(NULL, NULL);
/* Get the number of processes */
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
/* Get my rank among all the processes */
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
if (my_rank != 0) {
/* Create message */
sprintf(greeting, "Greetings from process %d of %d!",
my_rank, comm_sz);
/* Send message to process 0 */
MPI_Send(greeting, strlen(greeting)+1, MPI_CHAR, 0, 0,
MPI_COMM_WORLD);
} else {
/* Print my message */
printf("Greetings from process %d of %d!\n", my_rank, comm_sz);
for (int q = 1; q < comm_sz; q++) {
/* Receive message from process q */
MPI_Recv(greeting, MAX_STRING, MPI_CHAR, q,
0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/* Print message from process q */
printf("%s\n", greeting);
}
}
/* Shut down MPI */
MPI_Finalize();
return 0;
} /* main */
(2) 新增工具鏈
在設定 => 構建、執行、部署 => 工具鏈中新增一個工具鏈,型別選 WSL。
CLion 會自動檢測填寫,但 C 編譯器和 C++ 編譯器路徑需要手動指定,相應路徑使用 which 命令獲取:
# 獲取 C 編譯器路徑
which mpicc
# 獲取 C++ 編譯器路徑
which mpicxx
(3) 指定專案所用工具鏈
新增工具鏈後,必須手動設定專案使用新新增的工具鏈,在設定 => 構建、執行、部署 => CMake 中進行設定:
(4) 修改專案終端(可選)
在設定 => 工具 => 終端中,將 Shell 路徑修改為所用的發行版路徑,該路徑一般為:
# C:\Users\<你的電腦使用者名稱>\AppData\Local\Microsoft\WindowsApps\<發行版名稱>.exe,如:
C:\Users\username\AppData\Local\Microsoft\WindowsApps\ubuntu.exe
(5) 修改CMake配置檔案
修改專案根目錄下的 CMake 配置檔案 CMakeLists.txt:
- 在 add_executable 所在行之前新增:
find_package(MPI REQUIRED)
- 在 add_executable 所在行之後新增:
- 如果為 C 專案:
target_link_libraries(<your_project_name> PRIVATE MPI::MPI_C)
- 如果為 C++ 專案:
target_link_libraries(<your_project_name> PRIVATE MPI::MPI_CXX)
- 如果為 C 專案:
- 可能需要修改 cmake_minimum_required 中的版本號,使其不高於 WSL 中安裝的 CMake 版本
示例:
cmake_minimum_required(VERSION 3.22)
project(mpi)
set(CMAKE_C_STANDARD 11)
find_package(MPI REQUIRED)
add_executable(mpi main.c)
target_link_libraries(mpi PRIVATE MPI::MPI_C)
之後,重新載入 CMake 配置:
嘗試構建專案:
(6) 修改執行配置
為了可以一鍵執行,需要修改執行配置:
需要將可執行檔案修改為 mpiexec 的路徑(WSL 內透過 which 命令獲取),並將程式實參填寫為:-n 4 "$CMakeCurrentProductFile$"
,其中表示執行時使用 4 個程序,可根據需要修改
之後,點選執行,程式輸出如圖:
3. CLion除錯MPI程式
CLion 除錯 MPI 程式需要透過使用 GDB 的附加到程序功能間接實現。
(1) 新增斷點
在需要打斷點的位置打上斷點,並在所有斷點位置上方新增如下程式碼:
int i = 0;
while (!i)
sleep(5); // 需要包含unistd.h標頭檔案
之後在 sleep(5)
一行新增斷點。
(2) 執行程式
點選執行按鈕,執行程式。
(3) GDB附加到程序功能
點選工具 => 附加到程序:
在 SSH/WSL 中搜尋當前可執行檔案:
選取想要除錯的程序(在例項中,因為 0 號程序中未新增斷點,故不應選取程序號小的程序138113),點選使用WSL GDB附加,進入除錯模式。
為了程式可以向下執行,需要將 i 的值設定成 1。
之後正常除錯即可。
4. 後記
- 使用 CLion 只能透過 GDB 一個程序一個程序地除錯,未找到方法呼叫 TotalView 同時跟蹤和除錯多個程序
- 執行配置中的程式實參可以填寫為:
-n $Prompt$ "$CMakeCurrentProductFile$"
,實現每次執行前彈窗填寫需要執行的程序數
參考資料:
- How to work with OpenMPI projects in CLion | CLion Documentation
- Clion 可以編譯呼叫 MPI 並行庫的專案麼? - 知乎