Если вы посмотрите на свои задачи doWorkerTasks, вы увидите, что они отправляют ровно столько сообщений с данными, сколько они получают; (и они получают еще один, чтобы закрыть их).
Но ваш мастер-код:
for (int i = 1; i < np; i++) {
MPI_Send(¤tRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD);
rowsLeft--;
currentRow++;
}
for (;;) {
MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status);
if (rowsLeft == 0)
break;
MPI_Send(¤tRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD);
rowsLeft--;
currentRow++;
}
отправляет np-2 больше сообщений с данными, чем получает. В частности, он продолжает получать данные до тех пор, пока ему больше не нужно отправлять , хотя должно быть еще np-2 ожидающих сообщений данных. Меняем код на следующее:
int rowsLeftToSend= dimension;
int rowsLeftToReceive = dimension;
for (int i = 1; i < np; i++) {
MPI_Send(¤tRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD);
rowsLeftToSend--;
currentRow++;
}
while (rowsLeftToReceive > 0) {
MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status);
rowsLeftToReceive--;
if (rowsLeftToSend> 0) {
if (currentRow > 1004)
printf("Sending row %d to worker %d\n", currentRow, status.MPI_SOURCE);
MPI_Send(¤tRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD);
rowsLeftToSend--;
currentRow++;
}
}
Сейчас работает.
Почему код не блокируется (обратите внимание, что это тупик, а не условие гонки; это более распространенная параллельная ошибка в распределенных вычислениях) для сообщений меньшего размера - тонкая деталь того, как работает большинство реализаций MPI. Как правило, реализации MPI просто «проталкивают» небольшие сообщения по каналу независимо от того, готов ли получатель к ним или нет, но более крупные сообщения (поскольку они занимают больше ресурсов хранения на принимающей стороне) требуют некоторого рукопожатия между отправителем и получателем. (Если вы хотите узнать больше, ищите протоколы eager vs rendezvous).
Таким образом, для случая с маленьким сообщением (в этом случае менее 1006 дюймов, и 1 int тоже работает) рабочие узлы отправляли свои сообщения независимо от того, получал ли их мастер. Если бы мастер имел , вызванный MPI_Recv (), сообщения уже были бы там, и он немедленно вернулся бы. Но это не так, поэтому на главной стороне находились ожидающие сообщения; но это не имело значения. Мастер разослал свои сообщения об уничтожении, и все вышли.
Но для больших сообщений оставшимся send () необходимо, чтобы получатель принимал участие в очистке, а поскольку получатель никогда не делает этого, остальные работники зависают.
Обратите внимание, что даже для небольшого сообщения, в котором не было тупиков, код не работал должным образом - отсутствовали вычисленные данные.
Обновление : в вашем shutdownWorkers
была похожая проблема:
void shutdownWorkers() {
printf("All work has been done, shutting down clients now.\n");
for (int i = 0; i < np; i++) {
MPI_Send(0, 0, MPI_INT, i, TAG_FINISHOFF, MPI_COMM_WORLD);
}
}
Здесь вы отправляете всем процессам, включая ранг 0, тот, который выполняет отправку. В принципе, этот MPI_Send должен взаимоблокировать, поскольку это блокирующая отправка, и соответствующая запись уже не опубликована. Вы могли бы опубликовать неблокирующее получение раньше, чтобы избежать этого, но это не нужно - ранг 0 не должен сообщать о себе до конца. Так что просто измените цикл на
for (int i = 1; i < np; i++)
tl; dr - ваш код заблокирован, потому что мастер не получил достаточно сообщений от рабочих; это случилось для сообщений небольшого размера из-за деталей реализации, общих для большинства библиотек MPI.