我正在尝试将消息从某个进程发送到所有MPI进程,并且还试图从某个进程中的所有那些进程接收消息。基本上,这是所有通信的全部,其中每个进程向其他每个进程(除自身之外)发送消息并从其他每个进程接收消息。
以下示例代码段显示了我要实现的目标。现在,MPI_Send的问题是它的行为,对于较小的消息,它充当非阻塞,但对于较大的消息(在我的机器BUFFER_SIZE 16400中),它将阻塞。我知道这就是MPI_Send的行为。解决方法是,我将下面的代码替换为MPI_Sendrecv(send + recv)。示例代码如下:MPI_Sendrecv(intSendPack, BUFFER_SIZE, MPI_INT, processId, MPI_TAG, intReceivePack, BUFFER_SIZE, MPI_INT, processId, MPI_TAG, MPI_COMM_WORLD, MPI_STATUSES_IGNORE)
。我对每个级别的循环内的MPI_COMM_WORLD的所有过程进行了上述调用,这种方法给了我我想要实现的目标(所有通信)。但是,此调用需要花费大量时间,因此我想使用一些省时的方法减少调用时间。我尝试使用mpi散点图并聚集以执行所有通信,但是这里的一个问题是,在MPI_all_to_all函数调用的不同迭代中,缓冲区大小(16400)在实际实现中可能有所不同。在这里,我使用MPI_TAG来区分不同迭代中的调用,而我不能在分散和收集函数中使用它。
#define BUFFER_SIZE 16400
void MPI_all_to_all(int MPI_TAG)
{
int size;
int rank;
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
int* intSendPack = new int[BUFFER_SIZE]();
int* intReceivePack = new int[BUFFER_SIZE]();
for (int prId = 0; prId < size; prId++) {
if (prId != rank) {
MPI_Send(intSendPack, BUFFER_SIZE, MPI_INT, prId, MPI_TAG,
MPI_COMM_WORLD);
}
}
for (int sId = 0; sId < size; sId++) {
if (sId != rank) {
MPI_Recv(intReceivePack, BUFFER_SIZE, MPI_INT, sId, MPI_TAG,
MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
}
}
}
我想知道是否有一种方法可以使用任何有效的通信模型来执行所有通信。我不坚持使用MPI_Send,如果还有其他方法可以为我提供我想要实现的目标,那么我对此感到满意。任何帮助或建议,我们将不胜感激。
答案 0 :(得分:0)
这是一个基准,可以比较所有通信中集体通信与点对点通信的性能,
#include <iostream>
#include <algorithm>
#include <mpi.h>
#define BUFFER_SIZE 16384
void point2point(int*, int*, int, int);
int main(int argc, char *argv[])
{
MPI_Init(&argc, &argv);
int rank_id = 0, com_sz = 0;
double t0 = 0.0, tf = 0.0;
MPI_Comm_size(MPI_COMM_WORLD, &com_sz);
MPI_Comm_rank(MPI_COMM_WORLD, &rank_id);
int* intSendPack = new int[BUFFER_SIZE]();
int* result = new int[BUFFER_SIZE*com_sz]();
std::fill(intSendPack, intSendPack + BUFFER_SIZE, rank_id);
std::fill(result + BUFFER_SIZE*rank_id, result + BUFFER_SIZE*(rank_id+1), rank_id);
// Send-Receive
t0 = MPI_Wtime();
point2point(intSendPack, result, rank_id, com_sz);
MPI_Barrier(MPI_COMM_WORLD);
tf = MPI_Wtime();
if (!rank_id)
std::cout << "Send-receive time: " << tf - t0 << std::endl;
// Collective
std::fill(result, result + BUFFER_SIZE*com_sz, 0);
std::fill(result + BUFFER_SIZE*rank_id, result + BUFFER_SIZE*(rank_id+1), rank_id);
t0 = MPI_Wtime();
MPI_Allgather(intSendPack, BUFFER_SIZE, MPI_INT, result, BUFFER_SIZE, MPI_INT, MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
tf = MPI_Wtime();
if (!rank_id)
std::cout << "Allgather time: " << tf - t0 << std::endl;
MPI_Finalize();
delete[] intSendPack;
delete[] result;
return 0;
}
// Send/receive communication
void point2point(int* send_buf, int* result, int rank_id, int com_sz)
{
MPI_Status status;
// Exchange and store the data
for (int i=0; i<com_sz; i++){
if (i != rank_id){
MPI_Sendrecv(send_buf, BUFFER_SIZE, MPI_INT, i, 0,
result + i*BUFFER_SIZE, BUFFER_SIZE, MPI_INT, i, 0, MPI_COMM_WORLD, &status);
}
}
}
这里,每个等级在所有其他等级上都将自己的数组intSendPack
贡献给数组result
,而在所有等级上的结局应该相同。 result
是平坦的,每个等级从其BUFFER_SIZE
开始接受rank_id*BUFFER_SIZE
个条目。点对点通信后,将阵列重置为其原始形状。
时间是通过设置MPI_Barrier
来衡量的,它将为您提供所有等级中的最大时间。
我使用Nersc Cori KNL在slurm的1个节点上运行了基准测试。我为确保每种情况下的值均一致运行了几次,而我并没有查看异常值,但是您应该运行10次左右以收集更适当的统计信息。
这里有一些想法:
总而言之,请调整此基准以更紧密地表示您的代码并在系统上运行它,但是由于专用的优化(例如高级算法),在所有情况下或在所有情况下的集体通信应该更快用于通讯安排。 2-5倍的加速速度是相当可观的,因为交流通常对整体时间的贡献最大。