2012-12-24 35 views
1

我试图ISend()两个数组:arr1,arr2和整数n这是arr1,arr2大小。我从这个post了解到,发送一个包含全部三个结构的结构不是一个选项,因为n只在运行时才知道。显然,我需要首先接收n,否则接收过程不知道要接收多少个元素。什么是最有效的方法来实现这一点,而不使用blokcing Send()ÇMPI多个动态数组传递

回答

6

发送阵列的大小是冗余的(且低效的)作为MPI提供了一种探测进入的消息而没有接收它们,这在为了正确地分配存储器仅提供足够的信息。探测执行MPI_PROBE,看起来很像MPI_RECV,除了它没有缓冲区相关的参数。探测操作返回一个状态对象,然后可以查询可以从消息内容中提取的给定MPI数据类型的元素数量,其中MPI_GET_COUNT,因此显式发送元素数量变得多余。

下面是一个简单的例子有两个等级:

if (rank == 0) 
{ 
    MPI_Request req; 

    // Send a message to rank 1 
    MPI_Isend(arr1, n, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &req); 
    // Do not forget to complete the request! 
    MPI_Wait(&req, MPI_STATUS_IGNORE); 
} 
else if (rank == 1) 
{ 
    MPI_Status status; 

    // Wait for a message from rank 0 with tag 0 
    MPI_Probe(0, 0, MPI_COMM_WORLD, &status); 
    // Find out the number of elements in the message -> size goes to "n" 
    MPI_Get_count(&status, MPI_DOUBLE, &n); 
    // Allocate memory 
    arr1 = malloc(n*sizeof(double)); 
    // Receive the message. ignore the status 
    MPI_Recv(arr1, n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 
} 

MPI_PROBE还接受通配符排名MPI_ANY_SOURCE和通配符标签MPI_ANY_TAG。然后可以查阅状态结构中的相应条目,以便找出实际的发送者等级和实际的消息标签。

探测消息大小的工作原理是每个消息都带有一个标头,称为包络线。信封由发送者的等级,接收者的等级,消息标签和通信者组成。它还包含有关总邮件大小的信息。信封作为两个通信过程之间的初始握手的一部分发送。

+0

MPI_PROBE块操作吗?我会想象它。 – Shmoopy

+1

@Shmoopy,'MPI_PROBE'是一个阻塞操作。 'MPI_IPROBE'不会阻塞并返回一个布尔标志,指示匹配的消息是否立即可用。 –

0

首先,您需要为rank 0的arr1和arr2分配内存(full memory = n = elements),即您的前端处理器。

根据编号将数组分成若干部分。的处理器。确定每个处理器的元件数量。

发送该元件从等级0

第二发送是用于阵列即ARR1算到其它处理器和ARR2

在其它处理器根据从元件主收到的计分配ARR1和ARR2处理器即rank = 0.在接收到元素数后,接收分配的内存中的两个数组。

这是一个示例C++实现,但C将遵循相同的逻辑。也只是交换发送Isend。

#include <mpi.h> 
    #include <iostream> 

    using namespace std; 

    int main(int argc, char*argv[]) 
    { 
     MPI::Init (argc, argv); 

     int rank = MPI::COMM_WORLD.Get_rank(); 
     int no_of_processors = MPI::COMM_WORLD.Get_size(); 
     MPI::Status status; 

     double *arr1; 

     if (rank == 0) 
     { 
      // Setting some Random n 
      int n = 10; 

      arr1 = new double[n]; 

      for(int i = 0; i < n; i++) 
      { 
       arr1[i] = i; 
      } 

      int part = n/no_of_processors; 
      int offset = n % no_of_processors; 

      // cout << part << "\t" << offset << endl; 

      for(int i = 1; i < no_of_processors; i++) 
      { 
       int start = i*part; 
       int end  = start + part - 1; 

       if (i == (no_of_processors-1)) 
       { 
        end += offset; 
       } 

       // cout << i << " Start: " << start << " END: " << end; 

       // Element_Count 
       int e_count = end - start + 1; 

       // cout << " e_count: " << e_count << endl; 
       // Sending 
       MPI::COMM_WORLD.Send(
             &e_count, 
             1, 
             MPI::INT, 
             i, 
             0 
            ); 

       // Sending Arr1 
       MPI::COMM_WORLD.Send(
             (arr1+start), 
             e_count, 
             MPI::DOUBLE, 
             i, 
             1 
            ); 
      } 
     } 
     else 
     { 
      // Element Count 
      int e_count; 

      // Receiving elements count 
      MPI::COMM_WORLD.Recv ( 
            &e_count, 
            1, 
            MPI::INT, 
            0, 
            0, 
            status 
           ); 

      arr1 = new double [e_count]; 
      // Receiving FIrst Array 
      MPI::COMM_WORLD.Recv (
            arr1, 
            e_count, 
            MPI::DOUBLE, 
            0, 
            1, 
            status 
           ); 

      for(int i = 0; i < e_count; i++) 
      { 
       cout << arr1[i] << endl; 
      } 
     } 

     // if(rank == 0) 
     delete [] arr1; 

     MPI::Finalize(); 

     return 0; 
    } 
+0

谢谢!但你怎么能确定接收没有发生之前发生? – Shmoopy

+0

当您执行Send/Recv时,程序将启动处理器的句柄。 recv句柄等待相应的发送。该程序根据定义的参数(如data_type,no)匹配发送和接收。传递的元素,最重要的是你有一个用户定义的“标签”,它可以帮助映射这些语句。 Irecv/Isend和Recv/Send的唯一区别在于后面的代码会阻止代码的执行直到找到它的匹配部分,而Irecv/Isend放置一个句柄并继续移动到代码的其余部分。 – DOOM

+0

@DOOM,您关于匹配过程的陈述不正确。 MPI只匹配与接收方指定的包络过滤器一起发送的消息信封,并且只包括发件人的等级,标签和通信器。匹配消息时,不会使用数据类型和缓冲区大小。 –

0

@Histro我想说的是,Irecv/Isend是MPI lib自己操纵的一些函数。你问的问题完全取决于你关于Send/Recv之后你做什么的代码的其余部分。有两种情况:

  1. 硕士和工 您发送的问题的一部分(比如数组)的工人(所有其他队伍除了0 =主)。工作人员(在数组上)做了一些工作,然后将结果返回给主人。然后主人将结果加起来,并将新工作传达给工人。现在,在这里您希望主人等待所有工作人员返回他们的结果(修改后的数组)。所以你不能使用Isend和Irecv,而是在我的代码和相应的recv中使用多重发送。如果你的代码在这个方向上,你想使用B_cast和MPI_Reduce。

  2. 懒法师 主分工,但不小心从他的工人的结果。假设你想为相同的数据编程一个不同类型的模式。就像给定某个城市的人口特征一样,你想要计算的模式包括18岁以上有多少人,有多少人有工作,有多少人在某些公司工作。现在这些结果与彼此没有任何关系。在这种情况下,您不必担心工作人员是否收到数据。主人可以继续执行其余的代码。这是使用Isend/Irecv安全的地方。