簡體   English   中英

如何在MPI中傳遞2D數組並使用C語言創建動態標記值?

[英]How to pass 2D array in MPI and create a dynamic tag value using C language?

我是MPI編程的新手。 我有一個8乘10的數組,我需要用它來平行地找到每一行的總和。 在等級0(過程0)中,它將使用二維陣列生成8乘10的矩陣。 然后我會使用tag號作為數組的第一個索引值(行號)。 這樣,我可以使用唯一的緩沖區通過Isend發送。 但是,看起來我的Isend標簽號生成方法不起作用。 您能否查看以下代碼並告訴我是否正確傳遞2D數組和標簽號。 當我運行此代碼時,它會在執行runnk 1后停止並等待。 我在本例中使用了3個進程並使用命令mpirun -np 3 test請盡可能通過示例告訴我如何解決這個問題。

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

int main (int argc, char *argv[])
{
        MPI_Init(&argc, &argv);
        int world_rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
        int world_size;
        MPI_Comm_size(MPI_COMM_WORLD, &world_size);        
        int tag = 1;        
        int arr[8][10]; 
        MPI_Request request;
        MPI_Status status;
        int source = 0;
        int dest;

        printf ("\n--Current Rank: %d\n", world_rank);

        if (world_rank == 0)
        {
            int i = 0;
            int a, b, x, y;

            printf("* Rank 0 excecuting\n");

            for(a=0; a<8/(world_size-1); a++)//if -np is 3, this will loop 4 times
            {           
                for(b=0; b<(world_size-1); b++)//if -np is 3, this loops will loop 2 times
                {//So, if -np is 3, due to both of these loops, Isend will be called 8 times
                    dest = b+1;     
                    tag = a+b;//create a uniqe tag value each time, which can be use as first index value of array
                    //Error: This tag value passing to Isend doesn't seems to be workiing
                    MPI_Isend(&arr[tag][0], 10, MPI_INT, dest, tag, MPI_COMM_WORLD, &request);  
                }
            }

            for(x=0; x<8; x++)//Generating the whole 8 by 10 2D array
            {   
                i++;
                for ( y = 0; y < 10; y++ )
                {
                    arr[x][y] = i; 
                }   
            }               
        }
        else 
        {
            int a, b;                   
            for(b=1; b<=8/(world_size-1); b++)
            {
                int sum = 0;
                int i;
                MPI_Irecv(&arr[tag][0], 10, MPI_INT, source, tag, MPI_COMM_WORLD, &request);
                MPI_Wait (&request, &status);               
                        //Error: not getting the correct tag value
                for(i = 0; i<10; i++)
                {   
                    sum = arr[tag][i]+sum;
                }
                printf("\nSum is: %d at rank: %d and tag is:%d\n", sum, world_rank, tag);
            }           
        }
        MPI_Finalize();
}

標簽問題是因為標簽在不同進程上的計算方式(或不計算)。 您正在初始化所有進程的標記值

int tag = 1; 

然后,對於進程等級0,您將標記設置為

tag = a+b;

這是第一次設置,將tag設置為0,因為ab都從零開始。 但是,對於排名大於0的進程,標記永遠不會更改。 他們將繼續將標記設置為1。

標簽唯一標識由MPI_IsendMPI_Irecv發送的消息,這意味着發送及其相應的接收必須具有相同的標記才能使數據傳輸成功。 由於大多數接收的進程之間的標記不匹配,因此傳輸大多不成功。 這導致排名高於0的進程最終在調用MPI_Wait永遠阻塞(等待)。

為了解決這個問題,您必須確保更改排名大於零的進程的標記。 但是,在我們能夠做到這一點之前,還有其他一些值得探討的問題。

通過現在為rank 0進程設置標記的方式, tag只能擁有0到4的值(假設有3個進程)。 這是因為a限制在0到3的范圍內,而b只能有0或1的值。這些值的最大可能總和為4.這意味着當您使用arr[tag][0]訪問數組時,你會錯過很多數據,你會多次重復發送相同的行。 我建議您更改發送每個子數組的方式(您當前使用tag訪問它),這樣您只有一個for循環來確定要發送哪個子數組,而不是兩個嵌入式循環。 然后,您可以計算將數組發送到的進程

dest = subarray_index%(world_size - 1) + 1;

這將使等級大於零的過程之間的設置交替。 您可以將標記保持為subarray_index 在接收方,您需要按接收計算每個進程的標記。

最后,我發現您在發送數據后正在初始化陣列。 你想事先做到這一點。

結合所有這些方面,我們得到

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

int main (int argc, char *argv[])
{
        MPI_Init(&argc, &argv);
        int world_rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
        int world_size;
        MPI_Comm_size(MPI_COMM_WORLD, &world_size);        
        int tag = 1;        
        int arr[8][10]; 
        MPI_Request request;
        MPI_Status status;
        int source = 0;
        int dest;

        printf ("\n--Current Rank: %d\n", world_rank);

        if (world_rank == 0)
        {
            int i = 0;
            int a, b, x, y;

            printf("* Rank 0 excecuting\n");
            //I've moved the array generation to before the sends.
            for(x=0; x<8; x++)//Generating the whole 8 by 10 2D array
            {   
                i++;
                for ( y = 0; y < 10; y++ )
                {
                    arr[x][y] = i; 
                }   
            }

            //I added a subarray_index as mentioned above.
            int subarray_index;
            for(subarray_index=0; subarray_index < 8; subarray_index++)
            {
                dest = subarray_index%(world_size - 1) + 1;     
                tag = subarray_index;
                MPI_Isend(&arr[subarray_index][0], 10, MPI_INT, dest, tag, MPI_COMM_WORLD, &request);
            }

        }
        else 
        {
            int a, b;                   
            for(b=0; b<8/(world_size-1); b++)
            {
                int sum = 0;
                int i;
                //We have to do extra calculations here. These match tag, dest, and subarray.
                int my_offset = world_rank-1;
                tag = b*(world_size-1) + my_offset;
                int subarray = b;
                MPI_Irecv(&arr[subarray][0], 10, MPI_INT, source, tag, MPI_COMM_WORLD, &request);
                MPI_Wait (&request, &status);               
                for(i = 0; i<10; i++)
                {   
                    sum = arr[subarray][i]+sum;
                }
                printf("\nSum is: %d at rank: %d and tag is:%d\n", sum, world_rank, tag);
            }           
        }
        MPI_Finalize();
}

在這個版本中有一件事似乎還有點未完成供您考慮:如果您的流程數量發生變化會發生什么? 例如,如果您有4個進程而不是3個進程,則看起來您可能會遇到循環問題

for(b=0; b<8/(world_size-1); b++)

因為每個進程將執行相同的次數,但發送的數據量不會干凈地分配給3個工作者(非秩零進程)。

但是,如果您不關心這一點,那么您不需要處理此類情況。

除了一個顯而易見的問題:“你為什么要這樣做?”,這里有很多問題,我不確定我是否能夠列出所有問題。 我會嘗試一下:

  • 標簽:似乎您的方法的大部分是使用標簽作為查找接收器的位置的指示器。 但這里至少存在兩個主要缺陷:

    1. 由於tag在接收之前不知道,什么是&arr[tag][0]應該是什么?
    2. MPI中的標簽是消息“標識符”......在正常情況下,給定的通信(發送和匹配接收)應該具有匹配的標記。 這可以通過在接收端使用MPI_ANY_TAG特殊標記來緩解,並使用接收狀態的MPI_TAG字段檢索其實際值。 但這是另一個故事。

    這里的底線是該方法不是那么好。

  • 數據初始化:非阻塞MPI通信的主要原則之一是,您永遠不應該修改用於通信帖子MPI_Isend()此處為MPI_Isend() )與其終結(此處缺失)之間通信的緩沖區。 因此,您的數據生成必須嘗試傳遞數據之前發生。

  • 說到這一點,溝通最終確定:你完成了你的發送通信。 這可以使用wait-type調用( MPI_Wait()MPI_Waitall() ),或者測試類型調用的“無限”循環( MPI_Test()等)來完成...

  • MPI_Irecv() :為什么在下次調用MPI_Wait()時使用非阻塞接收? 如果您想要阻止接收,只需直接調用MPI_Recv()

所以從根本上說,你在這里嘗試做的事情看起來並不正確。 因此,我非常不願意向您提出更正版本,因為我不了解您嘗試解決的實際問題。 這段代碼是一個更大的真實版本(或者應該增長的東西的初始版本)的簡化版本,還是只是一個玩具示例,意味着你可以學習MPI發送/接收的工作方式? 你沒有使用像MPI_Scatter()這樣的集體通信的根本原因是什么?

根據您對這些問題的回答,我可以嘗試生成有效版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM