【问题标题】:C MPI multiple dynamic array passingC MPI 多动态数组传递
【发布时间】:2012-12-24 20:45:02
【问题描述】:

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

【问题讨论】:

    标签: mpi


    【解决方案1】:

    发送数组的大小是多余的(而且效率低下),因为 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。然后可以查询状态结构中的相应条目,以找出实际的发件人等级和实际的消息标签。

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

    【讨论】:

    • MPI_PROBE 是否阻止操作?我想是的。
    • @Shmoopy, MPI_PROBE 是一个阻塞操作。 MPI_IPROBE 不会阻塞并返回一个布尔标志,指示匹配的消息是否立即可用。
    【解决方案2】:

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

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

    将此元素计数从等级 0 发送到其他处理器。

    第二次发送用于数组,即 arr1 和 arr2

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

    这是一个示例 C++ 实现,但 C 将遵循相同的逻辑。也只需将 Send 与 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;
        }
    

    【讨论】:

    • 谢谢!但是你怎么能确定接收没有在发送之前发生呢?
    • 当您执行发送/接收时,程序会为处理器启动一个句柄。 recv 句柄等待相应的发送。该程序根据定义的参数(如 data_type、no)匹配发送和接收。传递的元素,最重要的是你有一个用户定义的“标签”,它可以帮助映射语句。 Irecv/Isend 和 Recv/Send 的唯一区别是后者会阻止代码的执行,直到找到匹配的部分,而 Irecv/Isend 放置一个句柄并继续移动到代码的其余部分。
    • @DOOM,您关于匹配过程的陈述不正确。 MPI 仅匹配使用接收者指定的包络过滤器发送的消息的包络,其中仅包括发送者的等级、标签和通信者。匹配消息时使用数据类型和缓冲区大小。
    【解决方案3】:

    @Histro 我想说的是,Irecv/Isend 是一些由 MPI lib 操作的函数。您提出的问题完全取决于您在发送/接收后所做的其余代码。有两种情况:

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

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

    【讨论】:

      猜你喜欢
      • 2012-10-12
      • 2014-04-18
      • 2021-12-15
      • 2017-09-20
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多