Hướng dẫn run mpi c++ - chạy mpi c ++

Nghiên cứu Điện toán Đại học Colorado Boulder

Các chương trình song song cho phép người dùng sử dụng đầy đủ cấu trúc đa nút của các cụm siêu máy tính. Giao diện truyền tin nhắn [MPI] là một tiêu chuẩn được sử dụng để cho phép một số bộ xử lý khác nhau trên một cụm liên lạc với nhau. Trong hướng dẫn này, chúng tôi sẽ sử dụng Trình biên dịch Intel C ++, GCC, IntelMPI và OpenMPI để tạo chương trình đa bộ xử lý ‘Hello World, trong C ++. Hướng dẫn này giả định người dùng có kinh nghiệm trong cả Terminal Linux và C ++.

Resources:

  • //www.dartmouth.edu/~rc/classes/intro_mpi/intro_mpi_overview.html
  • //mpitutorial.com/tutorials/
  • //condor.cc.ku.edu/~grobe/docs/intro-MPI-C.shtml

Thiết lập và "Xin chào, thế giới"

Bắt đầu bằng cách đăng nhập vào cụm và sử dụng SSH để đăng nhập vào một nút biên dịch. Điều này có thể được thực hiện với lệnh:

Tiếp theo chúng ta phải tải MPI vào môi trường của chúng ta. Bắt đầu bằng cách tải trong lựa chọn trình biên dịch C ++ và thư viện MPI tương ứng của nó. Sử dụng các lệnh sau nếu sử dụng trình biên dịch GNU C ++:

Trình biên dịch GNU C ++

module load gcc
module load openmpi

Hoặc, sử dụng các lệnh sau nếu bạn thích sử dụng trình biên dịch Intel C ++:

Trình biên dịch Intel C ++

module load intel
module load impi

Điều này sẽ chuẩn bị môi trường của bạn với tất cả các công cụ cần thiết để biên dịch và chạy mã MPI của bạn. Bây giờ, hãy bắt đầu xây dựng tệp C ++ của chúng tôi. Trong hướng dẫn này, chúng tôi sẽ đặt tên cho tệp mã của chúng tôi:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
3

Mở

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
3 và bắt đầu bằng cách bao gồm thư viện tiêu chuẩn C
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
5 và thư viện MPI
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
6 và bằng cách xây dựng chức năng chính của mã C ++:

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}

Bây giờ, hãy để Lừa thiết lập một số chỉ thị MPI để song song hóa mã của chúng tôi. Trong ‘Hello World, hướng dẫn, chúng tôi sẽ sử dụng bốn chỉ thị sau:

MPI_Init[]:

Hàm này khởi tạo môi trường MPI. Nó có trong các địa chỉ của các đối số dòng lệnh C ++ Argc và Argv.

MPI_Comm_size[]:

Hàm này trả về tổng kích thước của môi trường thông qua số lượng quy trình. Hàm có trong môi trường MPI và địa chỉ bộ nhớ của biến số nguyên.

MPI_Comm_rank[]:

Hàm này trả về ID quy trình của bộ xử lý được gọi là hàm. Hàm có trong môi trường MPI và địa chỉ bộ nhớ của biến số nguyên.

MPI_Finalize[]:

Chức năng này làm sạch môi trường MPI và kết thúc truyền thông MPI.

Bốn chỉ thị này là đủ để có được sự song song của chúng tôi, Hello World đang chạy. Chúng tôi sẽ bắt đầu bằng cách tạo

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
7,
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
8 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
9, để lưu trữ một định danh cho từng quy trình song song và số lượng quy trình chạy trong cụm tương ứng. Chúng tôi cũng sẽ thực hiện chức năng
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
0 sẽ khởi tạo Trình giao tiếp MPI:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}

Bây giờ, hãy để có được một số thông tin về cụm bộ xử lý của chúng tôi và in thông tin ra cho người dùng. Chúng tôi sẽ sử dụng các chức năng

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
1 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
2 để có được số lượng quy trình và thứ hạng của một quy trình tương ứng:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}

Cuối cùng, hãy để Lôi đóng môi trường bằng cách sử dụng

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
3:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_rank, size_Of_Cluster];

    MPI_Finalize[];
    return 0;
}

Bây giờ mã đã hoàn tất và sẵn sàng để được biên dịch. Bởi vì đây là một chương trình MPI, chúng tôi phải sử dụng một trình biên dịch chuyên dụng. Hãy chắc chắn sử dụng lệnh chính xác dựa trên trình biên dịch bạn đã tải.

OpenMPI

mpic++ hello_world_mpi.cpp -o hello_world_mpi.exe

Intel MPI

mpiicc hello_world_mpi.cpp -o hello_world_mpi.exe

Điều này sẽ tạo ra một thực thi mà chúng ta có thể vượt qua để lên đỉnh như một công việc. Để thực thi mã biên dịch MPI, phải sử dụng lệnh đặc biệt:

mpirun -np 4 ./hello_world_mpi.exe

Cờ -NP chỉ định số lượng bộ xử lý sẽ được sử dụng trong việc thực hiện chương trình.

Trong tập lệnh công việc của bạn, tải cùng một trình biên dịch và các lựa chọn OpenMPI mà bạn đã sử dụng ở trên để biên dịch chương trình và chạy công việc với SLURM để thực hiện ứng dụng. Kịch bản công việc của bạn sẽ trông giống như thế này:

OpenMPI

#!/bin/bash
#SBATCH -N 1
#SBATCH --ntasks 4
#SBATCH --job-name parallel_hello
#SBATCH --partition shas-testing
#SBATCH --time 0:01:00
#SBATCH --output parallel_hello_world.out

module purge

module load gcc
module load openmpi

mpirun -np 4 ./hello_world_mpi.exe

Intel MPI

module load intel
module load impi
0

Điều này sẽ tạo ra một thực thi mà chúng ta có thể vượt qua để lên đỉnh như một công việc. Để thực thi mã biên dịch MPI, phải sử dụng lệnh đặc biệt:

module load intel
module load impi
1

Cờ -NP chỉ định số lượng bộ xử lý sẽ được sử dụng trong việc thực hiện chương trình.

Trong tập lệnh công việc của bạn, tải cùng một trình biên dịch và các lựa chọn OpenMPI mà bạn đã sử dụng ở trên để biên dịch chương trình và chạy công việc với SLURM để thực hiện ứng dụng. Kịch bản công việc của bạn sẽ trông giống như thế này:

Điều quan trọng cần lưu ý là trên đỉnh, có tổng cộng 24 lõi mỗi nút. Đối với các ứng dụng yêu cầu hơn 24 quy trình, bạn sẽ cần yêu cầu nhiều nút trong công việc của mình. Tệp đầu ra của chúng tôi sẽ trông giống như thế này:

module load intel
module load impi
2

Tham khảo: //www.dartmouth.edu/~rc/ classes/intro_mpi/hello_world_ex.html

module load intel
module load impi
3

Tiếp theo, hãy để Lừa thực hiện một câu lệnh có điều kiện trong vòng lặp để chỉ in khi vòng lặp vòng khớp phù hợp với thứ hạng quy trình.

module load intel
module load impi
4

Cuối cùng, thực hiện chức năng rào cản trong vòng lặp. Điều này sẽ đảm bảo rằng tất cả các quá trình được đồng bộ hóa khi đi qua vòng lặp.

module load intel
module load impi
5

Biên dịch và chạy mã này sẽ dẫn đến đầu ra này:

module load intel
module load impi
6

Thông qua¶

Thông báo truyền là tiện ích chính trong giao diện ứng dụng MPI cho phép các quy trình giao tiếp với nhau. Trong hướng dẫn này, chúng tôi sẽ tìm hiểu những điều cơ bản về thông điệp vượt qua giữa 2 quy trình.

Thông báo truyền trong MPI được xử lý bởi các chức năng tương ứng và đối số của chúng:

module load intel
module load impi
7

Các đối số như sau:

MPI_Send

module load intel
module load impi
8

MPI_Recv

module load intel
module load impi
9

Hãy để thực hiện thông báo truyền qua một ví dụ:

Thí dụ¶

Chúng tôi sẽ tạo một quy trình hai quá trình sẽ chuyển số 42 từ quy trình này sang quy trình khác. Chúng tôi sẽ sử dụng chương trình Hello Hello World của chúng tôi như một điểm khởi đầu cho chương trình này. Hãy bắt đầu bằng cách tạo một biến để lưu trữ một số thông tin.

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
0

Bây giờ tạo các điều kiện

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
6 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
7 chỉ định quy trình thích hợp để gọi các hàm
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
8 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
9. Trong ví dụ này, chúng tôi muốn Quy trình 1 gửi một thông báo có chứa số nguyên 42 đến quy trình 2.

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
1

Cuối cùng, chúng ta phải gọi

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
8 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
9. Chúng tôi sẽ chuyển các tham số sau vào các chức năng:

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
2

Cho phép thực hiện các chức năng này trong mã của chúng tôi:

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
3

Biên dịch và chạy mã của chúng tôi với 2 quy trình sẽ dẫn đến đầu ra sau:

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
4

Các nhà khai thác nhóm: Phân tán và tập hợp

Các nhà khai thác nhóm rất hữu ích cho MPI. Chúng cho phép các dữ liệu được phân phối từ một quy trình gốc sang tất cả các quy trình có sẵn khác hoặc dữ liệu từ tất cả các quy trình có thể được thu thập tại một quy trình. Các nhà khai thác này có thể loại bỏ sự cần thiết của một lượng mã nồi hơi đáng ngạc nhiên thông qua việc sử dụng hai chức năng:

MPI_Scatter::

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
5

MPI_Gather::

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
6

Để hiểu rõ hơn về các chức năng này, hãy để Lôi đi trước và tạo một chương trình sẽ sử dụng chức năng phân tán. Lưu ý rằng hàm thu thập [không được hiển thị trong ví dụ] hoạt động tương tự và về cơ bản là đối nghịch của hàm phân tán. Các ví dụ khác sử dụng chức năng thu thập có thể được tìm thấy trong các hướng dẫn MPI được liệt kê là tài nguyên ở đầu tài liệu này.

Thí dụ¶

Chúng tôi sẽ tạo một quy trình hai quá trình sẽ chuyển số 42 từ quy trình này sang quy trình khác. Chúng tôi sẽ sử dụng chương trình Hello Hello World của chúng tôi như một điểm khởi đầu cho chương trình này. Hãy bắt đầu bằng cách tạo một biến để lưu trữ một số thông tin.

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
7

Bây giờ tạo các điều kiện

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
6 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
7 chỉ định quy trình thích hợp để gọi các hàm
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
8 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
9. Trong ví dụ này, chúng tôi muốn Quy trình 1 gửi một thông báo có chứa số nguyên 42 đến quy trình 2.

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_rank, size_Of_Cluster];

    MPI_Finalize[];
    return 0;
}
5:

#include 
#include 

int main[int argc, char** argv]{
    return 0;
}
8

Cuối cùng, chúng ta phải gọi

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
8 và
#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster;

    MPI_Init[&argc, &argv];
    MPI_Comm_size[MPI_COMM_WORLD, &size_Of_Cluster];
    MPI_Comm_rank[MPI_COMM_WORLD, &process_Rank];

    printf["Hello World from process %d of %d\n", process_Rank, size_Of_Cluster];

    return 0;
}
9. Chúng tôi sẽ chuyển các tham số sau vào các chức năng:

Cho phép thực hiện các chức năng này trong mã của chúng tôi:

Biên dịch và chạy mã của chúng tôi với 2 quy trình sẽ dẫn đến đầu ra sau:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
0

Các nhà khai thác nhóm: Phân tán và tập hợp

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
1

Các nhà khai thác nhóm rất hữu ích cho MPI. Chúng cho phép các dữ liệu được phân phối từ một quy trình gốc sang tất cả các quy trình có sẵn khác hoặc dữ liệu từ tất cả các quy trình có thể được thu thập tại một quy trình. Các nhà khai thác này có thể loại bỏ sự cần thiết của một lượng mã nồi hơi đáng ngạc nhiên thông qua việc sử dụng hai chức năng:

#include 
#include 

int main[int argc, char** argv]{
    int process_Rank, size_Of_Cluster

    MPI_Init[&argc, &argv];

    return 0;
}
2

Để hiểu rõ hơn về các chức năng này, hãy để Lôi đi trước và tạo một chương trình sẽ sử dụng chức năng phân tán. Lưu ý rằng hàm thu thập [không được hiển thị trong ví dụ] hoạt động tương tự và về cơ bản là đối nghịch của hàm phân tán. Các ví dụ khác sử dụng chức năng thu thập có thể được tìm thấy trong các hướng dẫn MPI được liệt kê là tài nguyên ở đầu tài liệu này.

Bài Viết Liên Quan

Chủ Đề