数据收发过程:
类似于Socket通信时,调用send将数据放置发送缓冲区即表示send完成。
该函数在发送消息时使用明确的缓冲区,并具有较低的内存使用率和较高的性能。相对于 MPI_Send 函数,MPI_Bsend 不阻塞发送方,也不会复制消息缓冲区中的数据,而是将数据拷贝到MPI缓冲区中,MPI_Bsend 函数将立即返回,在MPI缓冲区中的消息稍后使用异步方式传输。
函数原型
int MPI_Bsend(const void *buf, int count,
MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
//用户自行管理缓冲区
int MPI_Buffer_attach(void *buffer,int size)
int MPI_Buffer_detach(void *buffer,int &size)
参数详解
注意事项
发送时需要将数据从消息缓冲区拷贝到用户提供的缓冲区buffer,该方法消除了发送端同步的开销,如前面分析,消息发送能否进行及能否正确返回不依赖于接收进程。好处是用户可以认为程序需要发送的消息提供缓冲区,但用户也需要负责管理该缓冲区。如果该缓冲区buffer大小不足以存储消息缓冲区中待发送的数据,将导致程序错误退出。
#include
#include
#include
#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
int rank, size;
MPI_Status status;
double t0, t1;
char buf[BUFFER_SIZE];
char *my_buffer;
MPI_Request request;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if(rank == 0)
{
//为MPI缓冲区分配内存空间
my_buffer=(char *)malloc(BUFFER_SIZE);
MPI_Buffer_attach(my_buffer, BUFFER_SIZE);
snprintf(buf, BUFFER_SIZE, "Hello, world, from rank %d!", rank);
t0=MPI_Wtime();
//异步发送消息
MPI_Bsend(buf, BUFFER_SIZE, MPI_CHAR, 1, 0, MPI_COMM_WORLD, &request);
MPI_Wait(&request, &status);
t1=MPI_Wtime();
printf("Time taken=%f\n", t1-t0);
free(my_buffer);
MPI_Buffer_detach(&my_buffer, &BUFFER_SIZE);
}
else
{
//为MPI缓冲区分配内存空间
my_buffer=(char *)malloc(BUFFER_SIZE);
MPI_Buffer_attach(my_buffer, BUFFER_SIZE);
t0=MPI_Wtime();
//等待接收消息
MPI_Recv(buf, BUFFER_SIZE, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &status);
t1=MPI_Wtime();
printf("Rank %d received message=%s, time=%f\n", rank, buf, t1-t0);
free(my_buffer);
MPI_Buffer_detach(&my_buffer, &BUFFER_SIZE);
}
MPI_Finalize();
return 0;
}
该模式的开始不依赖接收进程相应的接收操作是否已经启动,但是同步发送却必须等到相应的接收进程开始后才可以正确返回。即
函数原型
int MPI_Ssend(void* buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
参数详解
代码示例
#include
#include
int main(int argc, char** argv) {
int size, rank;
int data;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
data = 12345;
MPI_Ssend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
printf("Process 0 sent data %d to process 1\n", data);
} else if (rank == 1) {
MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process 1 received data %d from process 0\n", data);
}
MPI_Finalize();
return 0;
}
该模式只有当接收进程的接收操作已经启动时才可以在发送进程启动发送操作(即是一种发送并立即返回操作),否则,当发送操作启动而相应的操作还没有启动时,发送操作将出错,对于非阻塞发送操作的正确返回,并不意味着发送已经完成,但对于阻塞发送的正确返回,则代表发送缓冲区可以重复使用。
函数原型
int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
参数详解
代码实例
#include
#include
int main(int argc, char** argv) {
int size, rank;
int data;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
data = 12345;
MPI_Request request;
MPI_Rsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
printf("Process 0 sent data %d to process 1\n", data);
} else if (rank == 1) {
MPI_Status status;
int data_recv;
MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process 1 received data %d from process 0\n", data_recv);
}
MPI_Finalize();
return 0;
}
是 MPI_Bsend 的非阻塞版本,在 MPI_Bsend 的基础上,MPI_Ibsend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Ibsend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型
int MPI_Ibsend(const void* buf, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm, MPI_Request* request)
参数详解
代码实例
#include
#include
#define BUFFER_SIZE 1024
int main(int argc, char** argv) {
int size, rank;
int data;
MPI_Status status;
char buffer[BUFFER_SIZE];
MPI_Request request;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
data = 12345;
MPI_Buffer_attach(buffer, BUFFER_SIZE);
MPI_Ibsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
printf("Process 0 sent data %d to process 1\n", data);
MPI_Wait(&request, &status);
MPI_Buffer_detach(buffer, &BUFFER_SIZE);
} else if (rank == 1) {
int data_recv;
MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process 1 received data %d from process 0\n", data_recv);
}
MPI_Finalize();
return 0;
}
MPI_Ssend 的非阻塞版本,在 MPI_Ssend 的基础上,MPI_Issend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Issend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型
int MPI_Issend(const void* buf, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm, MPI_Request* request)
参数详解
代码实例
#include
#include
#define BUFFER_SIZE 1024
int main(int argc, char** argv) {
int size, rank;
int data;
MPI_Status status;
char buffer[BUFFER_SIZE];
MPI_Request request;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
data = 12345;
MPI_Buffer_attach(buffer, BUFFER_SIZE);
MPI_Issend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
printf("Process 0 sent data %d to process 1\n", data);
MPI_Wait(&request, &status);
MPI_Buffer_detach(buffer, &BUFFER_SIZE);
} else if (rank == 1) {
int data_recv;
MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process 1 received data %d from process 0\n", data_recv);
}
MPI_Finalize();
return 0;
}
是 MPI_Rsend 的非阻塞版本,在 MPI_Rsend 的基础上,MPI_Irsend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Irsend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型
int MPI_Irsend(const void* buf, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm, MPI_Request* request)
参数详解
代码实例
#include
#include
#define BUFFER_SIZE 1024
int main(int argc, char** argv) {
int size, rank;
int data;
MPI_Status status;
char buffer[BUFFER_SIZE];
MPI_Request request;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
data = 12345;
MPI_Buffer_attach(buffer, BUFFER_SIZE);
MPI_Irsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
printf("Process 0 sent data %d to process 1\n", data);
MPI_Wait(&request, &status);
MPI_Buffer_detach(buffer, &BUFFER_SIZE);
} else if (rank == 1) {
int data_recv;
MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process 1 received data %d from process 0\n", data_recv);
}
MPI_Finalize();
return 0;
}
注,MPI_Irsend 的缓冲区大小由 MPI_BSEND_BUFFER_SIZE 宏定义所决定。如果发送的消息大于缓冲区大小,将会导致 MPI_Irsend 函数阻塞
短时间内有大量的短消息的发送
可能会导致缓冲区爆掉,导致消息丢失,用户可能需要使用MPI_Bsend控制缓冲区的使用;
如果短消息可以打包成一个大消息,降低发送次数
发送的数据量特别大
MPI提供的缓冲区的大小不够,可能导致数据丢失,从而导致程序卡,MPI_Bsend模式来直接控制缓冲区的使用;
可以进行分段发送,对数据进程拆分。