CLion和WSL配置MPI執行及除錯環境

BienBoy發表於2024-03-27

本文將介紹 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,將輸出:

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)
  • 可能需要修改 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 配置:

重新載入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$",實現每次執行前彈窗填寫需要執行的程序數

參考資料:

  1. How to work with OpenMPI projects in CLion | CLion Documentation
  2. Clion 可以編譯呼叫 MPI 並行庫的專案麼? - 知乎

相關文章