2017-05-17 8 views
1

병렬 행렬 행렬 곱셈에 대해 MPI_ScattervMPI_Gatherv 루틴을 구현했습니다. 이 크기를 초과하면 N = 180까지의 작은 매트릭스 크기에서 모든 것이 잘 작동합니다. N = 184 MPI는 MPI_Scatterv을 사용하는 동안 몇 가지 오류를 발생시킵니다.MPI_Scatterv/Gatherv가 "큰"2D 행렬로 MPI 오류를 던졌습니다.

2D Scatter에 대해서는 MPI_Type_create_subarrayMPI_TYPE_create_resized 인 구조를 사용했습니다. 이러한 구성에 대한 설명은 this question에서 찾을 수 있습니다.

필자가 작성한 최소한의 예제 코드는 행렬 A에 일부 값을 채우고 로컬 프로세스에 분산시키고 분산 된 A의 로컬 복사본에 각 프로세스의 순위 번호를 쓴다. 그런 다음 로컬 복사본이 마스터 프로세스. 내가 사용하는 경우

#include "mpi.h" 

#define N 184 // grid size 
#define procN 2 // size of process grid 

int main(int argc, char **argv) { 
    double* gA = nullptr; // pointer to array 
    int rank, size;  // rank of current process and no. of processes 

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

    // force to use correct number of processes 
    if (size != procN * procN) { 
     if (rank == 0) fprintf(stderr,"%s: Only works with np = %d.\n", argv[0], procN * procN); 
     MPI_Abort(MPI_COMM_WORLD,1); 
    } 

    // allocate and print global A at master process 
    if (rank == 0) { 
     gA = new double[N * N]; 
     for (int i = 0; i < N; i++) { 
      for (int j = 0; j < N; j++) { 
       gA[j * N + i] = j * N + i; 
      } 
     } 

     printf("A is:\n"); 
     for (int i = 0; i < N; i++) { 
      for (int j = 0; j < N; j++) { 
       printf("%f ", gA[j * N + i]); 
      } 
      printf("\n"); 
     } 
    } 

    // create local A on every process which we'll process 
    double* lA = new double[N/procN * N/procN]; 

    // create a datatype to describe the subarrays of the gA array 
    int sizes[2] = {N, N}; // gA size 
    int subsizes[2] = {N/procN, N/procN}; // lA 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_DOUBLE, &type); 
    MPI_Type_create_resized(type, 0, N/procN * sizeof(double), &subarrtype); 
    MPI_Type_commit(&subarrtype); 

    // compute number of send blocks 
    // compute distance between the send blocks 
    int sendcounts[procN * procN]; 
    int displs[procN * procN]; 

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

    // scatter global A to all processes 
    MPI_Scatterv(gA, sendcounts, displs, subarrtype, lA, 
       N*N/(procN*procN), MPI_DOUBLE, 
       0, MPI_COMM_WORLD); 

    // print local A's on every process 
    for (int p = 0; p < size; p++) { 
     if (rank == p) { 
      printf("la on rank %d:\n", rank); 
      for (int i = 0; i < N/procN; i++) { 
       for (int j = 0; j < N/procN; j++) { 
        printf("%f ", lA[j * N/procN + i]); 
       } 
       printf("\n"); 
      } 
     } 
     MPI_Barrier(MPI_COMM_WORLD); 
    } 
    MPI_Barrier(MPI_COMM_WORLD); 

    // write new values in local A's 
    for (int i = 0; i < N/procN; i++) { 
     for (int j = 0; j < N/procN; j++) { 
      lA[j * N/procN + i] = rank; 
     } 
    } 

    // gather all back to master process 
    MPI_Gatherv(lA, N*N/(procN*procN), MPI_DOUBLE, 
       gA, sendcounts, displs, subarrtype, 
       0, MPI_COMM_WORLD); 

    // print processed global A of process 0 
    if (rank == 0) { 
     printf("Processed gA is:\n"); 
     for (int i = 0; i < N; i++) { 
      for (int j = 0; j < N; j++) { 
       printf("%f ", gA[j * N + i]); 
      } 
      printf("\n"); 
     } 
    } 

    MPI_Type_free(&subarrtype); 

    if (rank == 0) { 
     delete gA; 
    } 

    delete lA; 

    MPI_Finalize(); 

    return 0; 
} 

그것은 컴파일 및 작은 N = 4의

mpicxx -std=c++11 -o test test.cpp 
mpirun -np 4 ./test 

를 사용하여 실행할 수 있습니다, ..., (180) 모두가 잘 여기 간다

A is: 
0.000000 6.000000 12.000000 18.000000 24.000000 30.000000 
1.000000 7.000000 13.000000 19.000000 25.000000 31.000000 
2.000000 8.000000 14.000000 20.000000 26.000000 32.000000 
3.000000 9.000000 15.000000 21.000000 27.000000 33.000000 
4.000000 10.000000 16.000000 22.000000 28.000000 34.000000 
5.000000 11.000000 17.000000 23.000000 29.000000 35.000000 
la on rank 0: 
0.000000 6.000000 12.000000 
1.000000 7.000000 13.000000 
2.000000 8.000000 14.000000 
la on rank 1: 
3.000000 9.000000 15.000000 
4.000000 10.000000 16.000000 
5.000000 11.000000 17.000000 
la on rank 2: 
18.000000 24.000000 30.000000 
19.000000 25.000000 31.000000 
20.000000 26.000000 32.000000 
la on rank 3: 
21.000000 27.000000 33.000000 
22.000000 28.000000 34.000000 
23.000000 29.000000 35.000000 
Processed gA is: 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 

당신은 오류가 표시 N = 184 :

Fatal error in PMPI_Scatterv: Other MPI error, error stack: 
PMPI_Scatterv(655)..............: MPI_Scatterv(sbuf=(nil), scnts=0x7ffee066bad0, displs=0x7ffee066bae0, dtype=USER<resized>, rbuf=0xe9e590, rcount=8464, MPI_DOUBLE, root=0, MPI_COMM_WORLD) failed 
MPIR_Scatterv_impl(205).........: fail failed 
I_MPIR_Scatterv_intra(265)......: Failure during collective 
I_MPIR_Scatterv_intra(259)......: fail failed 
MPIR_Scatterv(141)..............: fail failed 
MPIC_Recv(418)..................: fail failed 
MPIC_Wait(269)..................: fail failed 
PMPIDI_CH3I_Progress(623).......: fail failed 
pkt_RTS_handler(317)............: fail failed 
do_cts(662).....................: fail failed 
MPID_nem_lmt_dcp_start_recv(288): fail failed 
dcp_recv(154)...................: Internal MPI error! cannot read from remote process 
Fatal error in PMPI_Scatterv: Other MPI error, error stack: 
PMPI_Scatterv(655)..............: MPI_Scatterv(sbuf=(nil), scnts=0x7ffef0de9b50, displs=0x7ffef0de9b60, dtype=USER<resized>, rbuf=0x21a7610, rcount=8464, MPI_DOUBLE, root=0, MPI_COMM_WORLD) failed 
MPIR_Scatterv_impl(205).........: fail failed 
I_MPIR_Scatterv_intra(265)......: Failure during collective 
I_MPIR_Scatterv_intra(259)......: fail failed 
MPIR_Scatterv(141)..............: fail failed 
MPIC_Recv(418)..................: fail failed 
MPIC_Wait(269)..................: fail failed 
PMPIDI_CH3I_Progress(623).......: fail failed 
pkt_RTS_handler(317)............: fail failed 
do_cts(662).....................: fail failed 
MPID_nem_lmt_dcp_start_recv(288): fail failed 
dcp_recv(154)...................: Internal MPI error! cannot read from remote process 

내 생각 엔 서브 어레이를 사용하여 뭔가 잘못되었지만 왜 N = 4, ..., 180에서 작동합니까? 또 다른 가능성은 배열 데이터가 큰 데이터에 대해 선형이 아니므로 분산 형이 더 이상 작동하지 않는다는 것입니다. 캐시 크기 문제가 발생할 수 있습니까? 나는 MPI가 2D 배열을 흩어 낼 수 없다고 믿을 수 없다. N> 180 ...

나는 누군가가 나를 도울 수 있기를 바란다. 고마워!

+0

[this] (http://stackoverflow.com/questions/39548353/mpi-send-and-receive-dont-work) -with-more-then-8182-double) 도움? – Walter

+0

오, 처음에는 대답을 못 알아 냈습니다. 나는 그 문제가 발견 될 수 있다고 생각한다. 나는 인텔의 mpi 구현을 사용하며, 대규모 사용자 정의 데이터 유형에 대한 BCAST 기능에 문제가 있습니다. [참조] (https://software.intel.com/en-us/articles/intel-mpi-library-2017-known) -issue-mpi-bcast-hang-on-large-user-defined-datatypes)를 사용합니다. 하지만 난 큰 사용자 정의 데이터 형식에 대한 Scatterv에 대한 알려진 문제를 찾지 못했습니다 ... – JonasMu

답변

1

먼저 N이 작을 때 코드가 작동하지 않습니다. N = 6으로 설정하고 모든 항목이 고유하도록 매트릭스를 초기화하면 즉,

mpiexec -n 4 ./gathervorig 
A is: 
0.000000 6.000000 12.000000 18.000000 24.000000 30.000000 
1.000000 7.000000 13.000000 19.000000 25.000000 31.000000 
2.000000 8.000000 14.000000 20.000000 26.000000 32.000000 
3.000000 9.000000 15.000000 21.000000 27.000000 33.000000 
4.000000 10.000000 16.000000 22.000000 28.000000 34.000000 
5.000000 11.000000 17.000000 23.000000 29.000000 35.000000 
la on rank 0: 
0.000000 2.000000 7.000000 
1.000000 6.000000 8.000000 
2.000000 7.000000 12.000000 
la on rank 1: 
3.000000 5.000000 10.000000 
4.000000 9.000000 11.000000 
5.000000 10.000000 15.000000 
la on rank 2: 
18.000000 20.000000 25.000000 
19.000000 24.000000 26.000000 
20.000000 25.000000 30.000000 
la on rank 3: 
21.000000 23.000000 28.000000 
22.000000 27.000000 29.000000 
23.000000 28.000000 33.000000 

여기에 코드에서 오류가 아니라 인쇄에 : : 다음

gA[j * N + i] = j*N+i; 

당신이 볼 수있는 오류입니다

printf("%f ", lA[j * procN + i]); 

이어야

printf("%f ", lA[j * N/procN + i]); 

이제 t에 대한 정확한 답을 얻을 수 있습니다. 그는 분산 적어도 :

mpiexec -n 4 ./gathervorig 
A is: 
0.000000 6.000000 12.000000 18.000000 24.000000 30.000000 
1.000000 7.000000 13.000000 19.000000 25.000000 31.000000 
2.000000 8.000000 14.000000 20.000000 26.000000 32.000000 
3.000000 9.000000 15.000000 21.000000 27.000000 33.000000 
4.000000 10.000000 16.000000 22.000000 28.000000 34.000000 
5.000000 11.000000 17.000000 23.000000 29.000000 35.000000 
la on rank 0: 
0.000000 6.000000 12.000000 
1.000000 7.000000 13.000000 
2.000000 8.000000 14.000000 
la on rank 1: 
3.000000 9.000000 15.000000 
4.000000 10.000000 16.000000 
5.000000 11.000000 17.000000 
la on rank 2: 
18.000000 24.000000 30.000000 
19.000000 25.000000 31.000000 
20.000000 26.000000 32.000000 
la on rank 3: 
21.000000 27.000000 33.000000 
22.000000 28.000000 34.000000 
23.000000 29.000000 35.000000 

(가) 수집 비슷한 이유로 실패 - 로컬 초기화 :

lA[j * procN + i] = rank; 

이어야

lA[j * N/procN + i] = rank; 

을이 변경 후에는 작품도 수집 :

Processed gA is: 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
0.000000 0.000000 0.000000 2.000000 2.000000 2.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 
1.000000 1.000000 1.000000 3.000000 3.000000 3.000000 

여기서의 교훈은 항상 테스트 데이터입니다. i * j로 초기화하면 작은 시스템에서도 초기 오류를 발견하기 어렵습니다.

실제로 문제는 procN = N/procN = 2가되도록 N = 4로 설정한다는 것입니다. 항상 이상하거나 비정상적인 숫자로 이어지는 크기를 사용해보십시오. N = 6은 N/procN = 3이므로 procN = 2와의 혼동은 없습니다.

+0

도와 줘서 고맙습니다. 네가하는 모든 것이 진실이다. Ich는 언급 한 내용을 변경했지만 여전히 N = 184로 인해 코드가 실패합니다. N = 184로 업데이트 된 코드를 실행할 수 있습니까? – JonasMu

+0

이것은 MPI의 내부 또는 구성 오류와 같이 의심스러운 부분입니다. N = 180에서 분산 형의 각 개별 메시지는 64KB 미만이지만 N = 184는 64K 이상입니다. 더 많은 프로세스를 사용하여 N = 184를 실행할 수 있습니까? 4x4 격자? –

+0

예 4x4 proc 그리드를 사용하여 N = 360을 실행할 수 있습니다. 그럼 어떻게 처리해야합니까? – JonasMu