2012-02-13 3 views
43

2 차원 배열 블록을 다른 프로세서로 어떻게 보냅니 까? 2D 배열 크기가 400x400이고 서로 다른 프로세서에 100X100 크기의 블록을 보내려고한다고 가정합니다. 아이디어는 각 프로세서가 별도의 블록에서 계산을 수행하고 결과를 첫 번째 프로세서로 전송하여 최종 결과를 얻는 것입니다.
C 프로그램에서 MPI를 사용하고 있습니다.MPI를 사용하여 C로 2D 배열 블록 보내기

+2

은 ... – gsamaras

답변

106

먼저 "마스터"프로세스에서 거대한 데이터 덩어리를 산란시키고 수집하는 것이 일반적으로 바람직하지 않다고 말하며 시작하겠습니다. 일반적으로 각 작업이 자체 퍼즐 조각에서 벗어나고 싶기 때문에 한 프로세서에 전체 데이터의 "전체보기"가 필요하지 않도록해야합니다. 필요할 때마다 확장 성 및 문제점 크기를 제한합니다. 입출력을 위해이 작업을 수행하는 경우 - 한 프로세스가 데이터를 읽은 다음 데이터를 분산시킨 다음 다시 쓰는 방식으로 데이터를 수집합니다. 그러면 결국 MPI-IO를 조사하게됩니다.

MPI는 궁금한 점이 있으면 메모리에서 임의의 데이터를 가져 와서 프로세서 집합과 분산/수집하는 아주 좋은 방법이 있습니다. 불행히도 많은 수의 MPI 개념 (MPI 유형, 범위 및 집단 작업)이 필요합니다. 이 질문에 대한 답은 기본 아이디어가 많습니다 (MPI_Type_create_subarray and MPI_Gather).

업데이트 - 추운 날씨에, 이것은 많은 코드이며 많은 설명이 아닙니다. 그러니 조금 더 확장 시키십시오.

태스크 0이 다수의 MPI 태스크에 분배하기를 원하는 각각의 로컬 어레이에 조각을 갖도록하는 1d 정수 글로벌 배열을 고려하십시오. 네 가지 작업이 있으며 전역 배열이 [] 인 경우 당신은 작업 0이 이것을 배포하기 위해 4 개의 메시지 (자신에게 하나 포함)를 보낼 수 있으며, 다시 조립할 때 4 개의 메시지를 받아서 다시 묶을 수 있습니다. 하지만 분명히 많은 수의 프로세스에서 많은 시간이 소요됩니다. 이러한 종류의 작업, 즉 분산/수집 작업에 최적화 된 루틴이 있습니다. 이 같은 것을 할 줄이 1D 경우 그래서이 후

int global[8]; /* only task 0 has this */ 
int local[2]; /* everyone has this */ 
const int root = 0; /* the processor with the initial global data */ 

if (rank == root) { 
    for (int i=0; i<7; i++) global[i] = i; 
} 

MPI_Scatter(global, 2, MPI_INT,  /* send everyone 2 ints from global */ 
      local, 2, MPI_INT,  /* each proc receives 2 ints into local */ 
      root, MPI_COMM_WORLD); /* sending process is root, all procs in */ 
            /* MPI_COMM_WORLD participate */ 

를, 프로세서의 데이터가

task 0: local:[01] global: [] 
task 1: local:[23] global: [garbage-] 
task 2: local:[45] global: [garbage-] 
task 3: local:[67] global: [garbage-] 

과 같을 것이다, 산란 작업은 글로벌 배열을하고 연속 전송 2-int 청크를 모든 프로세서에 할당합니다.

for (int i=0; i<2; i++) 
    local[i] = local[i] + rank; 

MPI_Gather(local, 2, MPI_INT,  /* everyone sends 2 ints from local */ 
      global, 2, MPI_INT,  /* root receives 2 ints each proc into global */ 
      root, MPI_COMM_WORLD); /* recv'ing process is root, all procs in */ 
            /* MPI_COMM_WORLD participate */ 

이제 데이터가

task 0: local:[01] global: [0134679a] 
task 1: local:[34] global: [garbage-] 
task 2: local:[67] global: [garbage-] 
task 3: local:[9a] global: [garbage-] 

가 수집

처럼 보이는 모든 가져온다 :

배열을 다시 조립하기 위해, 우리는 정확하게 작동 동일하지만,에 리버스 MPI_Gather() 작업을 사용 데이터를 다시 가져오고, 여기에서 a는 10입니다. 왜냐하면이 예제를 시작할 때 조심스럽게 내 서식을 생각하지 않았기 때문입니다.

데이터 요소 수가 프로세스 수를 균등하게 나눠 내지 않으며 각 프로세스에 다른 수의 항목을 보내야하는 경우 어떻게됩니까? 그런 다음 각 프로세서의 개수와 displacements를 지정할 수있는 분산 형 버전 MPI_Scatterv()이 필요합니다. 전역 배열에서 데이터 조각이 시작됩니다. 예를 들어 9 자로 구성된 문자 배열 [abcdefghi]이 있다고 가정 해 봅시다. 마지막 프로세스를 제외하고 모든 프로세스에 2 개의 문자를 할당하려고했습니다.그럼 당신은

char global[9]; /* only task 0 has this */ 
char local[3]={'-','-','-'}; /* everyone has this */ 
int mynum;      /* how many items */ 
const int root = 0; /* the processor with the initial global data */ 

if (rank == 0) { 
    for (int i=0; i<8; i++) global[i] = 'a'+i; 
} 

int counts[4] = {2,2,2,3}; /* how many pieces of data everyone has */ 
mynum = counts[rank]; 
int displs[4] = {0,2,4,6}; /* the starting point of everyone's data */ 
          /* in the global array */ 

MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] pts from displs[i] */ 
      MPI_INT,  
      local, mynum, MPI_INT; /* I'm receiving mynum MPI_INTs into local */ 
      root, MPI_COMM_WORLD); 

이제 데이터가 이제 데이터의 불규칙한 금액을 배포하는 scatterv을 사용했습니다
task 0: local:[ab-] global: [abcdefghi] 
task 1: local:[cd-] global: [garbage--] 
task 2: local:[ef-] global: [garbage--] 
task 3: local:[ghi] global: [garbage--] 

처럼 보인다 필요 했어. 각 경우의 변위는 2 * 랭크 (문자로 측정되며, 변위는 분산 형 또는 집합체 용으로 전송되는 유형의 단위로, 일반적으로 바이트 또는 그 이상이 아닙니다) 배열의 시작부터이며, 개수는 {2,2,2,3}입니다. 우리가 3 문자를 원했던 첫 번째 프로세서라면 counts = {3,2,2,2}로 설정하고 displacements는 {0,3,5,7}이었을 것입니다. Gatherv는 다시 똑같이 작동하지만 반대합니다. counts 및 displs 배열은 동일하게 유지됩니다.

이제 2D의 경우 약간 더 까다 롭습니다. 2 차원 배열의 2 차원 서브 블록을 보내려면 우리가 보낸 데이터가 더 이상 연속적이지 않습니다. 우리가 4 개 프로세서에 6 × 6 어레이 (예를 들어) 3 × 3 서브 블록을 보내는 경우, 우리가 보내는 데이터는 구멍이 있습니다

2D Array 

    --------- 
    |000|111| 
    |000|111| 
    |000|111| 
    |---+---| 
    |222|333| 
    |222|333| 
    |222|333| 
    --------- 

Actual layout in memory 

    [000111000111000111222333222333222333] 

는 (모든 고성능 컴퓨팅 레이아웃을 이해하는 온다합니다

"1"로 표시된 데이터를 작업 1로 보내려면 3 개의 값을 건너 뛰고, 3 개의 값을 보내고, 3 개의 값을 건너 뛰고, 3 개의 값을 보내고, 3 개의 값을 건너 뛰고, 세 가지 값을 보냅니다. 두 번째 합병증은 소구역이 멈추고 시작되는 곳입니다. 영역 "1"은 영역 "0"이 멈춘 곳에서 시작하지 않습니다. 영역 "0"의 마지막 요소 다음에 메모리의 다음 위치는 영역 "1"을 통해 중간 경로입니다.

첫 번째 레이아웃 문제 - 우선 전송하려는 데이터 만 가져 오는 방법 -을 살펴 보겠습니다. 우리는 항상 모든 "0"지역 데이터를 다른 인접 배열로 복사하여 보낼 수 있습니다. 조심스럽게 계획을 세우면 결과에 MPI_Scatter을 부를 수 있습니다. 그러나 우리는 전체적으로 주요 데이터 구조를이 방식으로 바꾸지 않아도됩니다.

지금까지 우리가 사용한 모든 MPI 데이터 유형은 단순한 것입니다. MPI_INT는 연속으로 4 바이트를 지정합니다. 그러나 MPI를 사용하면 메모리에서 임의로 복잡한 데이터 레이아웃을 설명하는 고유 한 데이터 형식을 만들 수 있습니다. 그리고이 경우 - 배열의 직사각형 하위 영역 -은 일반적으로 충분하므로 그에 대한 구체적인 요구가 있습니다. 이 글로벌 어레이에서 단 영역 "0"을 집어 타입을 생성

MPI_Datatype newtype; 
    int sizes[2] = {6,6}; /* size of global array */ 
    int subsizes[2] = {3,3}; /* size of sub-region */ 
    int starts[2] = {0,0}; /* let's say we're looking at region "0", 
           which begins at index [0,0] */ 

    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &newtype); 
    MPI_Type_commit(&newtype); 

위에서 설명하는 2 차원 케이스와; 우리가 다른 프로세서

MPI_Send(&(global[0][0]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "0" */ 

로컬 배열로 나타날 수있는 수신 처리 이제 데이터의 단지 일부를 전송할 수있다. 수신 프로세스가 3x3 배열로만 수신되는 경우 이 아닌newtype 유형으로 수신되는 내용을 설명 할 수 있습니다. 더 이상 메모리 레이아웃을 설명하지 않습니다.우리는 또한, 하나 (다른 start 어레이)와 다른 형태를 만들어, 다른 서브 - 지역이 할 수

MPI_Recv(&(local[0][0]), 3*3, MPI_INT, 0, tag, MPI_COMM_WORLD); 

참고 용 대신, 단지 3 * 3 = 9 개의 정수의 블록을 수신하는 것 다른 블록, 또는 단지 특정 블록의 시작점에서 보내는 마지막으로

MPI_Send(&(global[0][3]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "1" */ 
    MPI_Send(&(global[3][0]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "2" */ 
    MPI_Send(&(global[3][3]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "3" */ 

, 우리는 여기에 메모리의 연속 된 덩어리가 될 글로벌 및 지역의 요구 있습니다; 즉, &(global[0][0])&(local[0][0]) (또는 동등하게는 *global*local은 연속적인 6 * 6 및 3 * 3 메모리 청크를 가리키며 동적 멀티 어레이를 할당하는 일반적인 방법으로는 보장되지 않습니다. 아래에

하위 영역을 지정하는 방법을 이해 했으므로 분산/수집 작업을 사용하기 전에 논의 할 사항이 하나 더 있습니다.이 유형의 "크기"입니다. MPI_Scatter() (또는 이 유형들은 16 개의 정수의 범위를 가지기 때문에 (즉, 시작한 후 16 개의 정수가 끝나는 곳, 그리고 끝이 다음 블록이 시작되는 곳과 잘 정렬되지 않기 때문에) 스 캐터 만 사용할 수는 없으며 다음 프로세서로 데이터를 보내기 시작하는 데 잘못된 위치를 선택하게됩니다.

물론 우리는 MPI_Scatterv()을 사용하고 직접 변위를 지정할 수 있습니다. 변위가 송신 유형 크기의 단위로되어 있고 이는 우리에게 도움이되지 않습니다. 블록은 전역 배열의 시작에서 (0,3,18,21) 정수의 오프셋에서 시작하며, 블록이 시작되는 곳에서 16 개의 정수를 끝내는 사실은 정수 배수로 이러한 변위를 전혀 표현하지 못하게합니다 .

이 문제를 해결하기 위해 MPI에서는 이러한 계산을 위해 유형의 범위를 설정할 수 있습니다. 유형을 자르지 않습니다. 이것은 단지 마지막 요소가 주어지면 다음 요소가 어디에서 시작되는지 알아 내는데 사용됩니다. 구멍이있는 이런 유형의 경우 메모리의 거리보다 실제 크기의 끝까지 크기를 작게 설정하는 것이 좋습니다.

범위를 우리에게 편리한 것으로 설정할 수 있습니다. 우리는 단지 범위 1의 정수를 만든 다음, 변위를 정수 단위로 설정할 수 있습니다. 이 경우 블록 3의 정수 - 하위 행의 크기 -를 설정하는 것을 좋아하지만, 블록 "1"은 블록 "0"바로 다음에 시작하고 블록 "3"은 블록 " 2 ". 불행히도 블록 2에서 블록 3으로 점프 할 때 잘 작동하지 않지만 도움이되지는 않습니다. 그래서

이 경우 서브 블록을 분산하기 위해 우리가 할 거라고 다음 여기에

MPI_Datatype type, resizedtype; 
    int sizes[2] = {6,6}; /* size of global array */ 
    int subsizes[2] = {3,3}; /* size of sub-region */ 
    int starts[2] = {0,0}; /* let's say we're looking at region "0", 
           which begins at index [0,0] */ 

    /* as before */ 
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type); 
    /* change the extent of the type */ 
    MPI_Type_create_resized(type, 0, 3*sizeof(int), &resizedtype); 
    MPI_Type_commit(&resizedtype); 

우리가 이전과 동일한 블록 타입을 만들었습니다,하지만 우리는 그것을 크기를 조정했습니다; 우리는 타입이 "시작되는"곳 (0)을 변경하지 않았지만 "끝나는 곳"(3 ints)을 변경했습니다. 전에 언급하지 않았지만, MPI_Type_commit이 타입을 사용할 수 있어야합니다. 중간 단계가 아니라 실제로 사용하는 최종 유형 만 커밋하면됩니다. 완료되면 MPI_Type_free을 사용하여 유형을 해제합니다.

은 그래서 지금, 마지막으로, 우리는 블록을 scatterv 수 있습니다 위의 데이터 조작이 조금 복잡하지만, 당신이 일을 끝낼되면, scatterv이 직전처럼 보이는 : 그리고 지금 우리가 완료

int counts[4] = {1,1,1,1}; /* how many pieces of data everyone has, in units of blocks */ 
int displs[4] = {0,1,6,7}; /* the starting point of everyone's data */ 
          /* in the global array, in block extents */ 

MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] types from displs[i] */ 
      resizedtype,  
      local, 3*3, MPI_INT; /* I'm receiving 3*3 MPI_INTs into local */ 
      root, MPI_COMM_WORLD); 

, 분산 형, 수집 형 및 MPI 파생 형을 약간 살펴본 후

문자 배열을 사용하여 gather 및 scatter 연산을 모두 보여주는 예제 코드는 다음과 같습니다.

$ mpirun -n 4 ./gathervarray 
Global array is: 

3456789012 
6789
9
2345678901 
56789
89
1234567890 
456789
789
Local process on rank 0 is: 
|| 
|34567| 
|67890| 
|90123| 
|23456| 
Local process on rank 1 is: 
|56789| 
|89012| 
|12345| 
|45678| 
|78901| 
Local process on rank 2 is: 
|56789| 
|89012| 
|12345| 
|45678| 
|78901| 
Local process on rank 3 is: 
|| 
|34567| 
|67890| 
|90123| 
|23456| 
Processed grid: 
AAAAABBBBB 
AAAAABBBBB 
AAAAABBBBB 
AAAAABBBBB 
AAAAABBBBB 
CCCCCDDDDD 
CCCCCDDDDD 
CCCCCDDDDD 
CCCCCDDDDD 
CCCCCDDDDD 

를하고 코드는 다음과 같습니다 프로그램을 실행합니다.

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

int malloc2dchar(char ***array, int n, int m) { 

    /* allocate the n*m contiguous items */ 
    char *p = (char *)malloc(n*m*sizeof(char)); 
    if (!p) return -1; 

    /* allocate the row pointers into the memory */ 
    (*array) = (char **)malloc(n*sizeof(char*)); 
    if (!(*array)) { 
     free(p); 
     return -1; 
    } 

    /* set up the pointers into the contiguous memory */ 
    for (int i=0; i<n; i++) 
     (*array)[i] = &(p[i*m]); 

    return 0; 
} 

int free2dchar(char ***array) { 
    /* free the memory - the first element of the array is at the start */ 
    free(&((*array)[0][0])); 

    /* free the pointers into the memory */ 
    free(*array); 

    return 0; 
} 

int main(int argc, char **argv) { 
    char **global, **local; 
    const int gridsize=10; // size of grid 
    const int procgridsize=2; // size of process grid 
    int rank, size;  // rank of current process and no. of processes 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 


    if (size != procgridsize*procgridsize) { 
     fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize); 
     MPI_Abort(MPI_COMM_WORLD,1); 
    } 


    if (rank == 0) { 
     /* fill in the array, and print it */ 
     malloc2dchar(&global, gridsize, gridsize); 
     for (int i=0; i<gridsize; i++) { 
      for (int j=0; j<gridsize; j++) 
       global[i][j] = '0'+(3*i+j)%10; 
     } 


     printf("Global array is:\n"); 
     for (int i=0; i<gridsize; i++) { 
      for (int j=0; j<gridsize; j++) 
       putchar(global[i][j]); 

      printf("\n"); 
     } 
    } 

    /* create the local array which we'll process */ 
    malloc2dchar(&local, gridsize/procgridsize, gridsize/procgridsize); 

    /* create a datatype to describe the subarrays of the global array */ 

    int sizes[2] = {gridsize, gridsize};   /* global size */ 
    int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize};  /* local size */ 
    int starts[2] = {0,0};      /* where this one starts */ 
    MPI_Datatype type, subarrtype; 
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_CHAR, &type); 
    MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(char), &subarrtype); 
    MPI_Type_commit(&subarrtype); 

    char *globalptr=NULL; 
    if (rank == 0) globalptr = &(global[0][0]); 

    /* scatter the array to all processors */ 
    int sendcounts[procgridsize*procgridsize]; 
    int displs[procgridsize*procgridsize]; 

    if (rank == 0) { 
     for (int i=0; i<procgridsize*procgridsize; i++) sendcounts[i] = 1; 
     int disp = 0; 
     for (int i=0; i<procgridsize; i++) { 
      for (int j=0; j<procgridsize; j++) { 
       displs[i*procgridsize+j] = disp; 
       disp += 1; 
      } 
      disp += ((gridsize/procgridsize)-1)*procgridsize; 
     } 
    } 


    MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]), 
       gridsize*gridsize/(procgridsize*procgridsize), MPI_CHAR, 
       0, MPI_COMM_WORLD); 

    /* now all processors print their local data: */ 

    for (int p=0; p<size; p++) { 
     if (rank == p) { 
      printf("Local process on rank %d is:\n", rank); 
      for (int i=0; i<gridsize/procgridsize; i++) { 
       putchar('|'); 
       for (int j=0; j<gridsize/procgridsize; j++) { 
        putchar(local[i][j]); 
       } 
       printf("|\n"); 
      } 
     } 
     MPI_Barrier(MPI_COMM_WORLD); 
    } 

    /* now each processor has its local array, and can process it */ 
    for (int i=0; i<gridsize/procgridsize; i++) { 
     for (int j=0; j<gridsize/procgridsize; j++) { 
      local[i][j] = 'A' + rank; 
     } 
    } 

    /* it all goes back to process 0 */ 
    MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize), MPI_CHAR, 
       globalptr, sendcounts, displs, subarrtype, 
       0, MPI_COMM_WORLD); 

    /* don't need the local data anymore */ 
    free2dchar(&local); 

    /* or the MPI data type */ 
    MPI_Type_free(&subarrtype); 

    if (rank == 0) { 
     printf("Processed grid:\n"); 
     for (int i=0; i<gridsize; i++) { 
      for (int j=0; j<gridsize; j++) { 
       putchar(global[i][j]); 
      } 
      printf("\n"); 
     } 

     free2dchar(&global); 
    } 


    MPI_Finalize(); 

    return 0; 
} 
+6

이 또 다시 여기에 몇 가지 버전에서 제공; 저는 우리가 사람들을 계속 지적 할 수있는 대답을 하나 쓰고 싶습니다. 하지만 고마워. :) –

+0

나는 Fortran MPI에 능숙하지만, 나중에 참조 할 때 이것을 즐겨 사용한다. 또한, 나는 두 번째 mort의 코멘트. – milancurcic

+0

이 모든 과정은 언어에 내장 된 다차원 배열을 가진 Fortran에서 더 쉽습니다. C가 포함하지 않기로 선택하는 것. 그리고 여러분 모두는 이미 매우 강한 대답을하고 있습니다. –

1

방금 ​​확인하면 쉽게 확인할 수 있습니다.

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

/* 
This is a version with integers, rather than char arrays, presented in this 
very good answer: http://stackoverflow.com/a/9271753/2411320 
It will initialize the 2D array, scatter it, increase every value by 1 and then gather it back. 
*/ 

int malloc2D(int ***array, int n, int m) { 
    int i; 
    /* allocate the n*m contiguous items */ 
    int *p = malloc(n*m*sizeof(int)); 
    if (!p) return -1; 

    /* allocate the row pointers into the memory */ 
    (*array) = malloc(n*sizeof(int*)); 
    if (!(*array)) { 
     free(p); 
     return -1; 
    } 

    /* set up the pointers into the contiguous memory */ 
    for (i=0; i<n; i++) 
     (*array)[i] = &(p[i*m]); 

    return 0; 
} 

int free2D(int ***array) { 
    /* free the memory - the first element of the array is at the start */ 
    free(&((*array)[0][0])); 

    /* free the pointers into the memory */ 
    free(*array); 

    return 0; 
} 

int main(int argc, char **argv) { 
    int **global, **local; 
    const int gridsize=4; // size of grid 
    const int procgridsize=2; // size of process grid 
    int rank, size;  // rank of current process and no. of processes 
    int i, j, p; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 


    if (size != procgridsize*procgridsize) { 
     fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize); 
     MPI_Abort(MPI_COMM_WORLD,1); 
    } 


    if (rank == 0) { 
     /* fill in the array, and print it */ 
     malloc2D(&global, gridsize, gridsize); 
     int counter = 0; 
     for (i=0; i<gridsize; i++) { 
      for (j=0; j<gridsize; j++) 
       global[i][j] = ++counter; 
     } 


     printf("Global array is:\n"); 
     for (i=0; i<gridsize; i++) { 
      for (j=0; j<gridsize; j++) { 
       printf("%2d ", global[i][j]); 
      } 
      printf("\n"); 
     } 
    } 
    //return; 

    /* create the local array which we'll process */ 
    malloc2D(&local, gridsize/procgridsize, gridsize/procgridsize); 

    /* create a datatype to describe the subarrays of the global array */ 
    int sizes[2] = {gridsize, gridsize};   /* global size */ 
    int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize};  /* local size */ 
    int starts[2] = {0,0};      /* where this one starts */ 
    MPI_Datatype type, subarrtype; 
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type); 
    MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(int), &subarrtype); 
    MPI_Type_commit(&subarrtype); 

    int *globalptr=NULL; 
    if (rank == 0) 
     globalptr = &(global[0][0]); 

    /* scatter the array to all processors */ 
    int sendcounts[procgridsize*procgridsize]; 
    int displs[procgridsize*procgridsize]; 

    if (rank == 0) { 
     for (i=0; i<procgridsize*procgridsize; i++) 
      sendcounts[i] = 1; 
     int disp = 0; 
     for (i=0; i<procgridsize; i++) { 
      for (j=0; j<procgridsize; j++) { 
       displs[i*procgridsize+j] = disp; 
       disp += 1; 
      } 
      disp += ((gridsize/procgridsize)-1)*procgridsize; 
     } 
    } 


    MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]), 
       gridsize*gridsize/(procgridsize*procgridsize), MPI_INT, 
       0, MPI_COMM_WORLD); 

    /* now all processors print their local data: */ 

    for (p=0; p<size; p++) { 
     if (rank == p) { 
      printf("Local process on rank %d is:\n", rank); 
      for (i=0; i<gridsize/procgridsize; i++) { 
       putchar('|'); 
       for (j=0; j<gridsize/procgridsize; j++) { 
        printf("%2d ", local[i][j]); 
       } 
       printf("|\n"); 
      } 
     } 
     MPI_Barrier(MPI_COMM_WORLD); 
    } 

    /* now each processor has its local array, and can process it */ 
    for (i=0; i<gridsize/procgridsize; i++) { 
     for (j=0; j<gridsize/procgridsize; j++) { 
      local[i][j] += 1; // increase by one the value 
     } 
    } 

    /* it all goes back to process 0 */ 
    MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize), MPI_INT, 
       globalptr, sendcounts, displs, subarrtype, 
       0, MPI_COMM_WORLD); 

    /* don't need the local data anymore */ 
    free2D(&local); 

    /* or the MPI data type */ 
    MPI_Type_free(&subarrtype); 

    if (rank == 0) { 
     printf("Processed grid:\n"); 
     for (i=0; i<gridsize; i++) { 
      for (j=0; j<gridsize; j++) { 
       printf("%2d ", global[i][j]); 
      } 
      printf("\n"); 
     } 

     free2D(&global); 
    } 


    MPI_Finalize(); 

    return 0; 
} 

출력 : 당신은 대답을 받아 들여야

linux16:>mpicc -o main main.c 
linux16:>mpiexec -n 4 main Global array is: 
1 2 3 4 
5 6 7 8 
9 10 11 12 
13 14 15 16 
Local process on rank 0 is: 
| 1 2 | 
| 5 6 | 
Local process on rank 1 is: 
| 3 4 | 
| 7 8 | 
Local process on rank 2 is: 
| 9 10 | 
|13 14 | 
Local process on rank 3 is: 
|11 12 | 
|15 16 | 
Processed grid: 
2 3 4 5 
6 7 8 9 
10 11 12 13 
14 15 16 17