2012-12-24 68 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安全的地方。