我可以捆绑两条MPI消息吗?

时间:2012-10-03 03:13:30

标签: c++ c cluster-computing mpi openmpi

我正在尝试无序地进行一对一的通信。基本上我有多个相同大小的浮点数组,由整数id标识。

每封邮件应如下所示:

<int id><float array data>

在接收器端,它确切知道有多少阵列,因此设置了确切的recv数。收到消息后,它会解析id并将数据放入正确的位置。问题是可以从任何其他进程向接收进程发送消息。 (例如,生产者有一个工作队列结构,并处理队列中可用的任何ID。)

由于MPI仅在订单传递中保证P2P,我不能轻易地将整数id和FP数据放在两个消息中,否则接收器可能无法将id与数据匹配。 MPI在一次发送中也不允许两种类型的数据。

我只能想到两种方法。

1)接收器有一个大小为m的数组(source [m]),m是发送节点的数量。发件人首先发送id,然后发送数据。接收方从接收方i收到整数消息后,将id保存到source [i]。从发送方i收到FP数组后,它检查source [i],获取id,并将数据移动到正确的位置。它的工作原理是因为MPI保证了有序的P2P通信。它要求接收方保留每个发送者的状态信息。更糟糕的是,如果单个发送进程在数据之前发送了两个id(例如多线程),则此机制将不起作用。

2)将id和FP视为字节,并将它们复制到发送缓冲区。将它们作为MPI_CHAR发送,接收器将它们转换回整数和FP数组。然后我需要支付将内容复制到发送方的字节缓冲区的额外成本。随着我在MPI进程中增加线程数,总临时缓冲区也会增长。

它们都不是完美的解决方案。我不想锁定进程内的任何内容。我想知道你们是否有更好的建议。

编辑:代码将在具有infiniband的共享群集上运行。这些机器将被随机分配。所以我不认为TCP套接字能够在这里帮助我。此外,IPoIB看起来很昂贵。我确实需要完整的40Gbps速率进行通信,并保持CPU进行计算。

2 个答案:

答案 0 :(得分:3)

您可以在接收函数中指定MPI_ANY_SOURCE作为源排名,然后使用其标记对消息进行排序,这比创建自定义消息更容易。这是一个简化的例子:

#include <stdio.h>
#include "mpi.h"

int main() {
    MPI_Init(NULL,NULL);
    int rank=0;
    int size=1;
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    // Receiver is the last node for simplicity in the arrays
    if (rank == size-1) {
        // Receiver has size-1 slots
        float data[size-1];
        MPI_Request request[size-1];

        // Use tags to sort receives
        for (int tag=0;tag<size-1;++tag){
            printf("Receiver for id %d\n",tag);
            // Non-blocking receive
            MPI_Irecv(data+tag,1,MPI_FLOAT,
                      MPI_ANY_SOURCE,tag,MPI_COMM_WORLD,&request[tag]);
        }

        // Wait for all requests to complete
        printf("Waiting...\n");
        MPI_Waitall(size-1,request,MPI_STATUSES_IGNORE);
        for (size_t i=0;i<size-1;++i){
            printf("%f\n",data[i]);
        }
    } else {
        // Producer
        int id = rank;
        float data = rank;
        printf("Sending {%d}{%f}\n",id,data);
        MPI_Send(&data,1,MPI_FLOAT,size-1,id,MPI_COMM_WORLD);
    }

    return MPI_Finalize();
}

答案 1 :(得分:3)

正如某人已经写过的那样,您可以使用MPI_ANY_SOURCE从任何来源接收。要在一次发送中发送两种不同类型的数据,您可以使用derived datatype

#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"

#define asize 10

typedef struct data_ {
  int   id;
  float array[asize];
} data;

int main() {

  MPI_Init(NULL,NULL);

  int rank = -1;
  int size = -1;
  MPI_Comm_rank(MPI_COMM_WORLD,&rank);
  MPI_Comm_size(MPI_COMM_WORLD,&size);

  data buffer;    
 // Define and commit a new datatype
  int          blocklength [2];
  MPI_Aint     displacement[2];
  MPI_Datatype datatypes   [2];
  MPI_Datatype mpi_tdata;

  MPI_Aint     startid,startarray;
  MPI_Get_address(&(buffer.id),&startid);
  MPI_Get_address(&(buffer.array[0]),&startarray);

  blocklength [0] = 1;
  blocklength [1] = asize;
  displacement[0] = 0;
  displacement[1] = startarray - startid;
  datatypes   [0] = MPI_INT;
  datatypes   [1] = MPI_FLOAT;

  MPI_Type_create_struct(2,blocklength,displacement,datatypes,&mpi_tdata);
  MPI_Type_commit(&mpi_tdata);

  if (rank == 0) {
    int        count = 0;
    MPI_Status status;

    while (count < size-1 ) {
      // Non-blocking receive
      printf("Receiving message %d\n",count);
      MPI_Recv(&buffer,1,mpi_tdata,MPI_ANY_SOURCE,0,MPI_COMM_WORLD,&status);
      printf("Message tag %d, first entry %g\n",buffer.id,buffer.array[0]);
      // Counting the received messages 
      count++;
    }

  } else {
    // Initialize buffer to be sent
    buffer.id = rank;
    for (int ii = 0; ii < size; ii++) {
      buffer.array[ii] = 10*rank + ii;
    }
    // Send buffer
    MPI_Send(&buffer,1,mpi_tdata,0,0,MPI_COMM_WORLD);
  }

  MPI_Type_free(&mpi_tdata);

  MPI_Finalize();
  return 0;
}