Sử dụng thư viện MPI
MPI là gì?
M P I = Message Passing Interface, là đặc tả cho người phát triển và thư viện lập trình truyền thông message sử dụng trong tính toán song song. Tập lệnh MPI thực thi bao gồm thư viện các thủ tục sao cho có thể gọi được từ các chương trình Fortran, C, C++ hay Ada.
Mô hình lập trình
- Ban đầu MPI được thiết kế cho các kiến trúc bộ nhớ phân tán, kiến trúc rất phổ biến thời kỳ 1980 đến đầu năm 1990.
- Xu hướng công nghệ thay đổi, bộ nhớ chia sẻ kết hợp với mạng máy tính tạo ra dạng lai của hai hệ thống bộ nhớ chia sẻ và bộ nhớ phân tán.
- Thực thi MPI tương thích với cả hai kiểu kiến trúc trên và cũng tương thích với các kiểu kết nối và giao thức khác nhau.
- Ngày nay MPI có thể chạy trên hầu hết các nền tảng phần cứng:
- Bộ nhớ chia sẻ
- Bộ nhớ phân tán
- Dạng lai hai loại trên
Cấu trúc chương trình MPI
Ví dụ 1: Khởi tạo môi trường, in ra id của các tác vụ
#include "mpi.h"
#include "stdio.h"
int main(int argc, char * argv[])
{
//Khoi tao moi truong cho MPI
MPI_Init(&argc,&argv);
int numtasks;//Chua so process trong group
int idtask;//Chua gia tri id cua moi process
MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&idtask);
//in ra id cua cac tac vu va so tac vu khoi tao
printf("Id tac vu: %d trong tong so: %d\n",idtask, numtasks);
//Giai phong moi truong MPI
MPI_Finalize();
return 0;
}
Tham khảo cách tích hợp MPI trong Visual Studio và cấu hình
Nhóm (group) và Comunicator
- Một nhóm là một tập có thế tự của các tiến trình. Mỗi tiến trình trong một nhóm được gắn với một số id duy nhất (rank). Các giá trị của rank bắt đầu từ 0 tới N-1, ở đây N là số tiến trình trong nhóm. Trong MPI, một nhóm được đại diện trong bộ nhớ hệ thống như một đối tượng. Nó có thể truy cập bởi các lập trình viện chỉ qua “handle”. Một nhóm luôn luôn gắn với một đối tượng communicator.
- Một communicator liên quan đến một nhóm các tiến trình mà có thể giao tiếp với nhau. Tất cả các message MPI phải chỉ định bởi một communicator. Giống như group, các communicator được mô tả trong bộ nhớ như các đối tượng và được truy cập bởi lập trình viên chỉ qua các “handle”. Ví dụ, handle cho một communicator là bao gồm tất cả các tác vụ của MPI_COMM_WORLD.
- Về khía cạnh lập trình viên, một nhóm và một communicator là một.
Ví dụ 2: Từ nhóm ban đầu, chia thành 2 nhóm riêng biệt và thực hiện truyền thông trên mỗi nhóm
#include "mpi.h"
#include "stdio.h"
#define NPROCS 8
int main(int argc, char *argv[]) {
int rank, new_rank, sendbuf, recvbuf, numtasks,
ranks1[4]={0,1,2,3}, ranks2[4]={4,5,6,7};
MPI_Group orig_group, new_group;
MPI_Comm new_comm;
//Khoi tao moi truong MPI
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks != NPROCS) {
printf("So tac vu nen = %d. Ket thuc.\n",NPROCS);
MPI_Finalize();
}
sendbuf = rank;
/* Lay ra handle cua nhom goc*/
MPI_Comm_group(MPI_COMM_WORLD, &orig_group);
/* Phan chi thanh 2 nhom rieng biet dua tren rank */
if (rank < NPROCS/2) {
MPI_Group_incl(orig_group, NPROCS/2, ranks1, &new_group);
}
else {
MPI_Group_incl(orig_group, NPROCS/2, ranks2, &new_group);
}
/* Tao mot communicator và thu thi cac truyen thong tap hop */
MPI_Comm_create(MPI_COMM_WORLD, new_group, &new_comm);
MPI_Allreduce(&sendbuf, &recvbuf, 1, MPI_INT, MPI_SUM, new_comm);
MPI_Group_rank (new_group, &new_rank);
printf("Id task= %d newrank= %d recvbuf= %d\n",rank,new_rank,recvbuf);
MPI_Finalize();
}
Truyền thông từ điểm tới điểm
Ví dụ 3: Thực hiện truyền thông có khoá gửi message ‘x’, ‘y’ từ tác vụ có id = 0 tới tác vụ 1 và ngược lại
#include "mpi.h"
#include "stdio.h"
int main(int argc, char* argv[])
{
int noProc, idProc;
char inmsg, outmsg = 'x';
int dest, source,tag = 1;
MPI_Status Stat;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&noProc);
MPI_Comm_rank(MPI_COMM_WORLD,&idProc);
//Gui msg tu tac vu co id = 0 toi tac vu co id = 1
if(idProc == 0)
{
//gui du lieu 'x' toi tac vu co id = 1
dest = 1;
source = 1;
MPI_Send(&outmsg,1,MPI_CHAR,dest,tag,MPI_COMM_WORLD);
//nhan du lieu 'y' tu tac vu co id = 1
MPI_Recv(&inmsg,1,MPI_CHAR,source,tag,MPI_COMM_WORLD,&Stat);
printf("ProcessID: %d, Message da nhan duoc:%c\n",idProc,inmsg);
}
if(idProc == 1)
{
source = 0;
dest = 0;
//nhan du lieu 'x' tu tac vu co id = 0
MPI_Recv(&inmsg,1,MPI_CHAR,source,tag,MPI_COMM_WORLD,&Stat);
printf("ProcessID: %d, Message da nhan duoc:%c\n",idProc,inmsg);
outmsg = 'y';
//gui du lieu 'y' toi tac vu co id = 0
MPI_Send(&outmsg,1,MPI_CHAR,dest,tag,MPI_COMM_WORLD);
}
//Giai phong MPI
MPI_Finalize();
return 0;
}
Ví dụ 4: Thực hiện truyền thông không khoá, gửi message vòng tròn từ tác vụ có id 0 -> 1 …-> n-1 và ngược lại
#include "stdio.h"
#include "mpi.h"
int main(int argc, char* argv[])
{
int noProc, idProc, next, prev, tag1 = 1, tag2 = 2;
char inmsg[2], outmsg[2];
MPI_Request reqs[4];
MPI_Status stats[4];
outmsg[0] = 'x';
outmsg[1] = 'y';
//Khoi tao moi truong MPI su dung cac doi so mac dinh cua ham main
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&noProc);
MPI_Comm_rank(MPI_COMM_WORLD, &idProc);
next = idProc + 1;
prev = idProc - 1;
if(idProc == 0) prev = noProc - 1;
if(idProc == (noProc -1)) next = 0;
//gui va nhan du lieu theo kieu non-blocking
MPI_Isend(&outmsg[0],1,MPI_CHAR,next,tag1,MPI_COMM_WORLD,&reqs[0]);
MPI_Irecv(&inmsg[0],1,MPI_CHAR,prev,tag1,MPI_COMM_WORLD, &reqs[1]);
MPI_Irecv(&inmsg[1],1,MPI_CHAR,next,tag2,MPI_COMM_WORLD, &reqs[2]);
MPI_Isend(&outmsg[1],1,MPI_CHAR,prev,tag2,MPI_COMM_WORLD, &reqs[3]);
//doi cac tien trinh nhan va gui message ket thuc
//MPI_Wait(&reqs,&stats);
MPI_Waitall(4,reqs,stats);
printf("ProcessId:%d, Message tag1:%c\n",idProc, inmsg[0]);
printf("ProcessId:%d, Message tag2:%c\n",idProc, inmsg[1]);
MPI_Finalize();
return 0;
}
Giải thích
Đoạn code trên thực hiện gửi message vòng tròn từ giữa các tác vụ, ví tụ tác vụ có id là i gửi dữ liệu tới tác vụ i+1 và nhận message từ i-1 và ngược lại.
Truyền thông tập hợp
Truyền thông tập hợp bao gồm tất cả các tiến trình (process) trong phạm vi của một commnicator.
- Tất cả các tiến trình là mặc định, các thành viên nằm trong một communicator MPI_COMM_WORLD.
- Bổ xung thêm các comminicators có thể được định nghĩa bởi lập trình viên.
- Truyền thông tập hợp có thể có kết quả không mong muốn, trong đó chương trình có thể thất bại có thể xảy ra nếu một tác vụ trong môt một communicator không tham dự.
Các kiểu truyền thông tập hợp:
- Đồng bộ – Các tiến trình đợi đến khi tất cả các thành viên trong nhóm đạt tới điểm đồng bộ.
- Di chuyển dữ liệu – broadcast, scatter/gather, all to all.
- Tính toán truyền thông (reductions) – một thành viên của nhóm thu thập dữ liệu từ các thành viên khác và thực hiện các thao tác (min, max, cộng, nhân, v.v.) trên dữ liệu.
Ví dụ 5: Thực hiện truyền thông tập hợp gửi dữ liệu từ một tác tới các tác vụ còn lại
#include "mpi.h"
#include "stdio.h"
#define SIZE 4
int main(int argc, char* argv[])
{
int numtasks, rank, sendcount, recvcount, source;
float sendbuf[SIZE][SIZE] = {
{1.0, 2.0, 3.0, 4.0},
{5.0, 6.0, 7.0, 8.0},
{9.0, 10.0, 11.0, 12.0},
{13.0, 14.0, 15.0, 16.0} };
float recvbuf[SIZE];
//Khoi tao moi truong
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE) {
source = 1;
sendcount = SIZE;
recvcount = SIZE;
//Gui du lieu tu tac vu co id = 1 toi cac tac vu khac
MPI_Scatter(sendbuf,sendcount,MPI_FLOAT,recvbuf,recvcount,
MPI_FLOAT,source,MPI_COMM_WORLD);
//In ra du lieu trong cac tac vu
printf("Id task= %d ket qua: %f %f %f %f\n",rank,recvbuf[0],
recvbuf[1],recvbuf[2],recvbuf[3]);
}
else
printf("So tac su dung nen bang: %d. Ket thuc.\n",SIZE);
MPI_Finalize();
}
Bài tập lập trình song song
Tính toán số PI
Có rất nhiều thuật toán và cách thức tính số PI, ngày này với sự trợ giúp của máy tính người ta tính số PI với độ chính xác rất cao.
- Giá trị số PI có thể được tính theo nhiều cách khác nhau. Xem xét phương pháp tính số PI sau:
- Thuật giải tuần tự:
npoints = 10000
circle_count = 0
do j = 1,npoints
phát sinh 2 số ngẫu nhiên trong khoảng 0 và 1
xcoordinate = random1
ycoordinate = random2
if (xcoordinate, ycoordinate) bên trong hình tròn
then circle_count = circle_count + 1
end do
PI = 4.0*circle_count/npoints
- Chiến thuật song song: ngắt vòng lặp thành các phần mà có thể được thực thi bởi các tác vụ khác nhau
- Thuật giải song song
npoints = 10000
circle_count = 0
p = số tác vụ
num = npoints/p
kiểm tra xem if tôi là MASTER hay WORKER
do j = 1,num
phát sinh 2 số ngẫu nhiên trong khoảng 0 và 1
xcoordinate = random1
ycoordinate = random2
if (xcoordinate, ycoordinate) bên trong hình tròn
then circle_count = circle_count + 1
end do
if Tôi là MASTER
nhận về từ các WORKER số circle_count
tính toán PI (sử dụng MASTER và các tính toán của WORKER)
else if tôi là WORKER
gửi tới MASTER circle_count
endif
1 phản hồi
[…] Sử dụng thư viên MPI và các ví dụ minh hoạ […]