MPI_Sendrecv тупики на 3+ процессов - PullRequest
3 голосов
/ 27 июня 2011

Попытка сделать обмен строк "гало / призрак", и я зашел в тупик (во фрагменте кода под картинкой). Строки «гало», подлежащие обмену, представлены в виде темно-серых линий (на рисунке) и в виде hp[0] и hp[M-1] (в коде).

[Невозможно опубликовать фотографии; недостаточно репутации. Снова в словах: hp[0] и hp[M-1] - строки "гало" (то есть строки, подлежащие обмену), в то время как hp[1] и hp[M-2] (и все промежуточные строки) должны быть рассчитаны с помощью.]

Почему этот фрагмент (который работает нормально для 2 процессов) блокируется с 3+ процессами?

// in-between processes ("P1" and "P2" in the picture; 
// one of "P1" and "P2" is of course missing in the case of 3 processes)
if (p > 0 && p < P-1) 
{ 
    MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0, 
                 hp[0],   N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);  
    MPI_Sendrecv(hp[1],   N, MPI_DOUBLE, p-1, 1, 
                 hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);  
}
// root process ("P0" in the picture)
else if (p == 0) 
{
    MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0, 
                 hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);  
}
// last process ("P3" in the picture)
else 
{
    MPI_Sendrecv(hp[1],   N, MPI_DOUBLE, p-1, 1, 
                 hp[0],   N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s); 
}

Платформа: Windows XP с DeinoMPI GUI с кнопкой "Show Messages", которая "Interrupts the running job and prints the current state of the message queues"

Ну, это пример "текущего состояния" (в тупике):

Rank 0 queues:
 Posted receive queue:
  rank=2, tag=1, context_id=1(Collective), count=0, dtype=MPI_BYTE
Rank 1 queues:
 Posted receive queue:
  rank=0, tag=0, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE
 Received but unmatched queue:
  rank=2, tag=2, context_id=MPI_COMM_WORLD, length=80
  rank=2, tag=2, context_id=MPI_COMM_WORLD, length=80
  rank=0, tag=1, context_id=1(Collective), length=0
Rank 2 queues:
 Posted receive queue:
  rank=1, tag=1, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE

Почему существует MPI_BYTE в качестве типа данных и 1(Collective) в качестве контекста? И почему Rank 0 в его очереди приема rank = 2?!

PS: Простите, если я спрашиваю (и упускаю) что-то очевидное, но я уже прочитал слишком много вопросов SO, увы, решения пока не найдено. Так много, что я знаю трио HPC Джонатана Дурси, High Performance Mark и suszterpatt.

Обновление (полный цикл)

В цикле не так много всего, поэтому я могу опубликовать его целиком: он содержит несколько прокомментированных MPI_Barrier s, потому что я произвольно пытался определить, какая комбинация подойдет (поговорим о "черном ящике" «). Таким образом, кроме этих MPI_Barrier s (и MPI_Sccaterv перед циклом) не происходит никакой другой связи. В целях тестирования я делаю return 0; до MPI_Gatherv после цикла (так что это также не должно иметь последствий для тупиков).

while (1)
{
    difference = 0.0;

    //MPI_Barrier(MPI_COMM_WORLD);

    // in-between processes ("P1" and "P2" in the picture; 
    // one of "P1" and "P2" is of course missing in the case of 3 processes)
    if (p > 0 && p < P-1) 
    { 
        MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0, 
                     hp[0],   N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);  
        MPI_Sendrecv(hp[1],   N, MPI_DOUBLE, p-1, 1, 
                     hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);  
    }
    // root process ("P0" in the picture)
    else if (p == 0) 
    {
        MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0, 
                     hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);  
    }
    // last process ("P3" in the picture)
    else 
    {
        MPI_Sendrecv(hp[1],   N, MPI_DOUBLE, p-1, 1, 
                     hp[0],   N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s); 
    }
    //MPI_Barrier(MPI_COMM_WORLD);

    // calculate "hpNEW" for each inner point
    for (y = 1; y < M-1; ++y)
        for (x = 1; x < N-1; ++x)
        {
            hpNEW[y][x] = (hp[y][x-1] + hp[y][x+1] + hp[y-1][x] + hp[y+1][x]) / 4.0;
            if (fabs( hpNEW[y][x] - hp[y][x] ) > diff)
                difference = fabs(hpNEW[y][x] - hp[y][x]);
        }

    if (difference < EPSILON)
        break;

    // transfer "hpNEW"'s calculated inner points to "hp" for next iteration 
    for (y = 1; y < M-1; ++y)
        for (x = 1; x < N-1; ++x)
            hp[y][x] = hpNEW[y][x];
} // while END

Один процесс действительно break выйдет из цикла первым ... будет / может ли это вызвать тупик (помимо прочего, без моего ведома)? Если так, как это предотвратить?

Еще одна вещь, касающаяся "странных" tag с. Я просто запустил вышеуказанный цикл со всеми MPI_Barrier закомментированными ... и получил это "странное" (есть tag=4!) Состояние очереди сообщений:

Rank 0 queues:
 Posted receive queue:
  rank=1, tag=4, context_id=1(Collective), count=30, dtype=MPI_DOUBLE
 Received but unmatched queue:
  rank=2, tag=1, context_id=1(Collective), length=0
Rank 1 queues:
 Posted receive queue:
  rank=0, tag=0, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE
 Received but unmatched queue:
  rank=2, tag=1, context_id=MPI_COMM_WORLD, length=80
Rank 2 queues:
 Posted receive queue:
  rank=1, tag=1, context_id=1(Collective), count=0, dtype=MPI_BYTE

1 Ответ

5 голосов
/ 27 июня 2011

Есть другие, мы только те, которые были активны в последнее время ...

Интересно, что такое DeinoMPI для Windows, я не знал, что у него есть хорошие инструменты, чтобы увидеть, что происходит в реальностивремя такое.

Так что вы определенно не спрашиваете что-то очевидное;на первый взгляд, я не вижу ничего плохого в коде, который вы разместили.Лично я считаю более понятным использование чего-то вроде MPI_PROC_NULL для упрощения логики кода:

left = p-1;
if (left < 0) left = MPI_PROC_NULL;
right = p+1;
if (right >= P) right = MPI_PROC_NULL;

MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, right, 0, 
             hp[0],   N, MPI_DOUBLE, left , 0, MPI_COMM_WORLD, &s);  
MPI_Sendrecv(hp[1],   N, MPI_DOUBLE, left , 1, 
             hp[M-1], N, MPI_DOUBLE, right, 1, MPI_COMM_WORLD, &s);  

и позволить библиотеке MPI иметь дело с крайними случаями, вместо того, чтобы иметь явные тесты if (p == 0) и т. Д .;но это вопрос вкуса, и что вы собираетесь делать с кодом потом.

Ситуация в очередях сообщений достаточно запутанная, и я не думаю, что код, который вы разместили, отвечает затупик, хотя это может быть где (скажем) ранг 1 в конечном итоге проявляет тупик - это может быть там, где видится зависание ранга 1.

Если вы посмотрите на то, что происходит, ранг 1 ждет 10удваивается от ранга 0, а ранг 2 ждет 10 удвоений от ранга 1, так что это похоже на фазу отправления направо вашего заполнения гало - 1 и 2 опубликовали свои соответствующие приемы для этой фазы - за исключением того, что тег 2 неверен,он получает 10 удвоений с тегом 1, что не должно происходить (по приведенному выше коду).

Кроме того, ранг 0 ожидает завершения этого коллектива (с нулевыми данными, связанными с ним - aбарьер, может быть? Или MPI_Finalize или что-то еще с подразумеваемой синхронизацией?) и поэтому не отправляет 1;У ранга 1 уже есть сообщение как часть этого коллектива, поэтому, если он закончится, он сразу же очистит это и сделает его частью этого коллектива.У него также есть два сообщения, которые уже находятся там с рангом 2, с тегом 2?Так что это должно происходить на другом этапе коммуникации вне текущего фрагмента кода.

Просто догадываясь из того, что я вижу в очереди, я предполагаю, что код выглядит примерно так:

loop { 
    communication as posted above;

    another phase of communication;

    synchronization (barrier?)
}

и это та вторая фаза связи, которая имеет незначительную ошибку.

Обновление :

Хорошо, поэтому процессы, выходящие из цикла в разное время, будут определенно вызывать блокировки по мере запуска процессовжду сообщений, которые никогда не придут от соседей.Но это легко исправить;после того как вы локально рассчитаете наибольшую разницу, вы найдете максимальное значение этой разности для процессоров с MPI_Allreduce;только если глобальная разница между hp и hpNEW везде меньше EPSILON, вы продолжаете.

// calculate "hpNEW" for each inner point locally
for (y = 1; y < M-1; ++y)
    for (x = 1; x < N-1; ++x)
    {
        hpNEW[y][x] = (hp[y][x-1] + hp[y][x+1] + hp[y-1][x] + hp[y+1][x]) / 4.0;
        if (fabs( hpNEW[y][x] - hp[y][x] ) > diff)
            diff = fabs(hpNEW[y][x] - hp[y][x]);
    }

// find the maximum of all the local differences

MPI_Allreduce (&diff, &globaldiff, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);

if (globaldiff < EPSILON)
    break;
...