Как передать 2D-массив в MPI и создать динамическое значение тега с использованием языка C?

Я новичок в программировании MPI. У меня есть массив размером 8 на 10, который мне нужно использовать, чтобы найти суммирование каждой строки параллельно. В ранге 0 (процесс 0) он будет генерировать матрицу 8 на 10, используя 2-мерный массив. Тогда я бы использовал номер tag как первое значение индекса (номер строки) массива. Таким образом, я могу использовать уникальный буфер для отправки через Isend. Тем не менее, похоже, что мой метод генерации номера тега для Isend не работает. Не могли бы вы взглянуть на следующий код и сказать, правильно ли я передаю 2D-массив и номер тега. Когда я запускаю этот код, он останавливается сразу после запуска rannk 1 и ждет. Я использую 3 процесса для этого примера и использую команду mpirun -np 3 test пожалуйста, дайте мне знать, как решить эту проблему с примером, если это возможно.

 #include "mpi.h" #include  #include  int main (int argc, char *argv[]) { MPI_Init(&argc, &argv); int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int tag = 1; int arr[8][10]; MPI_Request request; MPI_Status status; int source = 0; int dest; printf ("\n--Current Rank: %d\n", world_rank); if (world_rank == 0) { int i = 0; int a, b, x, y; printf("* Rank 0 excecuting\n"); for(a=0; a<8/(world_size-1); a++)//if -np is 3, this will loop 4 times { for(b=0; b<(world_size-1); b++)//if -np is 3, this loops will loop 2 times {//So, if -np is 3, due to both of these loops, Isend will be called 8 times dest = b+1; tag = a+b;//create a uniqe tag value each time, which can be use as first index value of array //Error: This tag value passing to Isend doesn't seems to be workiing MPI_Isend(&arr[tag][0], 10, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); } } for(x=0; x<8; x++)//Generating the whole 8 by 10 2D array { i++; for ( y = 0; y < 10; y++ ) { arr[x][y] = i; } } } else { int a, b; for(b=1; b<=8/(world_size-1); b++) { int sum = 0; int i; MPI_Irecv(&arr[tag][0], 10, MPI_INT, source, tag, MPI_COMM_WORLD, &request); MPI_Wait (&request, &status); //Error: not getting the correct tag value for(i = 0; i<10; i++) { sum = arr[tag][i]+sum; } printf("\nSum is: %d at rank: %d and tag is:%d\n", sum, world_rank, tag); } } MPI_Finalize(); } 

Проблема с тегом связана с тем, как тег вычисляется (или нет) на разных процессах. Вы инициализируете значения тегов для всех процессов как

 int tag = 1; 

и позже, для ранга процесса 0, вы устанавливаете тег

 tag = a+b; 

который в первый раз будет установлен, установит tag в 0, так как и a и b начинаются как ноль. Однако для процессов с рангом выше 0 тег никогда не изменяется. Они будут по-прежнему иметь тег, установленный в 1.

Тег уникально идентифицирует сообщение, отправляемое MPI_Isend и MPI_Irecv , что означает, что для отправки и соответствующего ему приема должен иметь тот же тег для успешной передачи данных. Поскольку tags несовместимы между процессами для большинства получателей, передачи в основном не увенчались успехом. Это вызывает процессы с рангом выше 0, чтобы в конечном итоге заблокировать (ждать) на вызов MPI_Wait .

Чтобы исправить это, вы должны обязательно изменить tags для процессов с рангом выше нуля. Однако, прежде чем мы сможем это сделать, есть еще несколько вопросов, которые стоит подправить.

Так как теперь вы установили тэг для процесса ранга 0, tag может иметь значения от 0 до 4 (при условии 3 процессов). Это связано с тем, что a ограничено диапазоном от 0 до 3, а b может иметь только значения 0 или 1. Максимально возможная сумма этих значений равна 4. Это означает, что при доступе к вашему массиву с помощью arr[tag][0] вы пропустите много данных, и вы будете повторно отправлять те же строки несколько раз. Я рекомендую изменить подход к отправке каждого подмассива (к которому вы в настоящее время обращаетесь с tag ), так что у вас есть только один для цикла, чтобы определить, какой субмассив отправить, а не два встроенных цикла. Затем вы можете рассчитать процесс отправки массива как

 dest = subarray_index%(world_size - 1) + 1; 

Это приведет к чередованию desitnations между процессами с рангом больше нуля. Вы можете сохранить тег как только subarray_index . На принимающей стороне вам нужно будет вычислить тег для каждого процесса за каждый прием.

Наконец, я увидел, что вы инициализировали массив после отправки данных. Вы хотите сделать это заранее.

Объединяя все эти аспекты, мы получаем

 #include "mpi.h" #include  #include  int main (int argc, char *argv[]) { MPI_Init(&argc, &argv); int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int tag = 1; int arr[8][10]; MPI_Request request; MPI_Status status; int source = 0; int dest; printf ("\n--Current Rank: %d\n", world_rank); if (world_rank == 0) { int i = 0; int a, b, x, y; printf("* Rank 0 excecuting\n"); //I've moved the array generation to before the sends. for(x=0; x<8; x++)//Generating the whole 8 by 10 2D array { i++; for ( y = 0; y < 10; y++ ) { arr[x][y] = i; } } //I added a subarray_index as mentioned above. int subarray_index; for(subarray_index=0; subarray_index < 8; subarray_index++) { dest = subarray_index%(world_size - 1) + 1; tag = subarray_index; MPI_Isend(&arr[subarray_index][0], 10, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); } } else { int a, b; for(b=0; b<8/(world_size-1); b++) { int sum = 0; int i; //We have to do extra calculations here. These match tag, dest, and subarray. int my_offset = world_rank-1; tag = b*(world_size-1) + my_offset; int subarray = b; MPI_Irecv(&arr[subarray][0], 10, MPI_INT, source, tag, MPI_COMM_WORLD, &request); MPI_Wait (&request, &status); for(i = 0; i<10; i++) { sum = arr[subarray][i]+sum; } printf("\nSum is: %d at rank: %d and tag is:%d\n", sum, world_rank, tag); } } MPI_Finalize(); } 

Есть одна вещь, которая по-прежнему кажется немного незавершенной в этой версии, чтобы вы подумали: что произойдет, если ваше количество процессов изменится? Например, если у вас есть 4 процесса вместо 3, похоже, что вы можете столкнуться с некоторыми проблемами с циклом

 for(b=0; b<8/(world_size-1); b++) 

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

Однако, если это вас не касается, вам не нужно обращаться с такими случаями.

Помимо очевидного вопроса: «Почему бы вам это сделать?», Здесь так много проблем, что я не уверен, что смогу перечислить их все. Я попробую хотя бы:

  • Тег: кажется, что основная часть вашего метода заключается в использовании тега в качестве индикатора того, где искать приемник. Но есть (по крайней мере) два основных недостатка:

    1. Так как tag не знает до получения, что такое &arr[tag][0] ?
    2. Теги в MPI – это «идентификатор» сообщений … При нормальных обстоятельствах данное сообщение (отправка и сопоставление) должно иметь соответствующий тег. Это можно облегчить, используя специальный тег MPI_ANY_TAG на принимающей стороне и получить его фактическое значение, используя поле MPI_TAG статуса приема. Но это уже другая история.

    Нижняя линия здесь заключается в том, что метод не такой хороший.

  • Инициация данных: одним из основных принципов неблокирующей коммуникации MPI является то, что вы никогда не должны изменять буфер, который вы использовали для связи между сообщением связи (здесь MPI_Isend() ) и его завершение (которое здесь отсутствует). Поэтому генерация данных должна происходить до попыток передачи данных.

  • Говоря об этом, завершение связи: вы также завершаете отправку сообщений. Это можно сделать, используя либо вызов типа ожидания ( MPI_Wait() или MPI_Waitall() ), либо «бесконечный» цикл вызовов тестового типа ( MPI_Test() и т. Д.) …

  • MPI_Irecv() : почему вы используете неблокирующий прием, когда самый следующий вызов – MPI_Wait() ? Если вы хотите заблокировать получение, просто вызовите MPI_Recv() напрямую.

Так что принципиально, что вы пытаетесь сделать здесь, выглядит не так. Поэтому я очень неохотно пытаюсь предложить вам исправленную версию, так как я не понимаю, какую проблему вы пытаетесь решить. Является ли этот код уменьшенной версией более крупного реального (или начальной версии чего-то, что должно расти), или просто игрушечный пример, предназначенный для вас, чтобы узнать, как работает передача / прием MPI? Есть ли какая-то фундаментальная причина, почему вы не используете коллективное общение, такое как MPI_Scatter() ?

В зависимости от вашего ответа по этим вопросам я могу попытаться создать допустимую версию.