C MPI多动态数组传递

时间:2012-12-24 20:45:02

标签: mpi

我正在尝试ISend()两个数组:arr1,arr2和整数n,其大小为arr1,arr2。我从这个post中了解到,发送包含所有三个结构的结构不是一个选项,因为n仅在运行时已知。显然,我需要首先接收n,否则接收过程将不知道要接收多少元素。如果不使用blokcing Send(),最有效的方法是什么?

3 个答案:

答案 0 :(得分:6)

发送数组的大小是冗余的(并且效率低下),因为MPI提供了一种探测传入消息而不接收它们的方法,它提供了足够的信息以便正确分配内存。探测是使用MPI_PROBE执行的,它看起来很像MPI_RECV,除了它不需要与缓冲区相关的参数。探测操作返回一个状态对象,然后可以查询给定MPI数据类型的元素数量,该数据类型可以使用MPI_GET_COUNT从消息内容中提取,因此显式发送元素数量变得多余。 / p>

这是一个有两个等级的简单示例:

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。然后,可以查询状态结构中的相应条目,以找出实际的发件人等级和实际的消息标记。

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

答案 1 :(得分:0)

首先,您需要将内存(完整内存= n =元素)分配给arr1和arr2,等级为0.即您的前端处理器。

根据编号将数组分成几部分。处理器。确定每个处理器的元素数。

将此元素计数发送到排名为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;
    }

答案 2 :(得分:0)

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

  1. 大师和工人 您将部分问题(例如数组)发送给工人(除了0 = Master之外的所有其他等级)。工作人员做了一些工作(在数组上)然后将结果返回给主人。然后,主人将结果加起来,并将新工作传达给工人。现在,在这里,您希望主服务器等待所有工作程序返回其结果(已修改的数组)。所以你不能使用Isend和Irecv,而是使用我的代码和相应的recv中的多次发送。如果您的代码朝这个方向发展,那么您想使用B_cast和MPI_Reduce。

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