MPI通信類型:
- 點對點通信
- 聚合通信
點對點通信:
- 同一通信器內的兩個進程之間的消息傳遞;
- 分爲阻塞型、非阻塞型通信。
本文主要學習點對點通信:
MPI點對點消息發送模式
- 標準模式(學習重點)
自由發送接收,不考慮其它進程狀態;
緩存模式
由用戶顯式提供緩存區,輔助通信;創建、釋放緩存區:
MPI_Buffer_attach(buffer, size);
MPI_Buffer_detach(buffer, size);同步模式
通信雙方先建立聯繫,再通信;
就緒模式
接受進程必須先於發送進程提出通信要求;
MPI標準阻塞通信函數
MPI消息由消息信封和消息數據組成
int MPI_Send(
void *send_buffer,/*指向包含消息內容的內存塊的指針 */
int send_count,/*數據量*/
MPI_Datatype datatype,/*數據類型*/
int dest,/*要接收消息的進程的進程號*/
int tag,/*tag=0,表示消息是要打印的;tag=1,表示消息是要用於計算的*/
MPI_Comm comm);/*通信器,指定通信範圍*/
前三項爲待發送的消息數據,後三項爲消息信封(接收者的地址)。
int MPI_Recv(void *recv_buffer,int recv_count,MPI_Datatype datatype,int source,int tag,MPI_Comm comm,MPI_Status * status);
注:
1.若接收多個進程傳來的消息,int source使用常量MPI_ANY_SOURCE則可以按照進程完成工作的順序來接收結果,避免等待;
2.Recv的tag要與Send的tag相匹配;
3.若一個進程接收多條來自另一個進程的有着不同tag的消息,可將常量MPI_ANY_TAG傳遞給tag;
4.status簡介
類型爲MPI_Status的結構體,至少有以下三個成員
status.MPI_SOURCE
status.MPI_TAG
status.MPI_ERROR
將&status作爲最後一個參數傳遞給Recv函數並調用它後,可以通過檢查結構體中的成員來確定發送者source和標籤tag。
進一步解釋:
MPI_Send發送過程:
1.發送進程組裝消息(包括數據和信封)
2.緩衝消息/阻塞
- 緩衝消息:MPI把消息放置在自己內部存儲器裏,並返回MPI_Send調用;
- 阻塞(block):發送進程一直等待,直到可以發送消息,並不立即返回MPI_Send調用;
MPI_Recv接收過程:
總是阻塞的直到接收到一條匹配的信息。
陷阱:
- 若MPI_Send發生阻塞,並且沒有相匹配的進程接收,那麼發送進程懸掛;
- 若MPI_Send緩衝,但沒有相匹配的接收,那麼信息丟失。
用MPI實現梯形積分
/*
梯形積分法,計算y=sin x 在[0,pi]上的積分
@ trap 梯形積分串行程序
@total_inte 最終積分結果
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include<math.h>
#include "mpi.h"
using namespace std;
const double a = 0.0;
const double b = 3.1415926;
int n = 100;
double h = (b - a) / n;
double trap(double a, double b, int n, double h)
{
double*x = new double[n + 1];
double*f = new double[n + 1];
double inte = (sin(a) + sin(b)) / 2;
for (int i = 1; i<n + 1; i++) {
x[i] = x[i - 1] + h; /*x_0=a,x_n=b*/
f[i] = sin(x[i]);
inte += f[i];
}
inte = inte*h; /* inte=h*[f(a)/2+f(x_1)+...f(x_{n-1})+f(b)/2]*/
return inte;
}
int main(int argc, char * argv[])
{
int myid, nprocs;
int local_n;
double local_a;
double local_b;
double total_inte;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid); /* get current process id */
MPI_Comm_size(MPI_COMM_WORLD, &nprocs); /* get number of processes */
local_n = n / nprocs;
local_a = a + myid*local_n*h;
local_b = local_a + local_n*h;
double local_inte = trap(local_a, local_b, local_n, h);
if (myid != 0)
{
MPI_Send(&local_inte, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
else
{
total_inte = local_inte;
for (int i = 1; i<nprocs; i++)
{
MPI_Recv(&local_inte, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
total_inte += local_inte;
}
}
if (myid == 0)
{
printf("integral output is %d", total_inte);
}
MPI_Finalize();
return 0;
}