Я только учусь OpenMPI. Попробовал простой MPI_Scatter
пример:
#include <mpi.h>
using namespace std;
int main() {
int numProcs, rank;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
int* data;
int num;
data = new int[5];
data[0] = 0;
data[1] = 1;
data[2] = 2;
data[3] = 3;
data[4] = 4;
MPI_Scatter(data, 5, MPI_INT, &num, 5, MPI_INT, 0, MPI_COMM_WORLD);
cout << rank << " recieved " << num << endl;
MPI_Finalize();
return 0;
}
Но это не сработало, как ожидалось …
Я ожидал что-то вроде
0 received 0
1 received 1
2 received 2 ...
Но то, что я получил, было
32609 received
1761637486 received
1 received
33 received
1601007716 received
Что со странными рядами? Кажется, что-то связано с моим разбросом? Кроме того, почему sendcount
а также recvcount
тот же самый? Сначала я подумал, так как я разбрасываю 5 элементов на 5 процессоров, каждый получит 1? Так что я должен использовать:
MPI_Scatter(data, 5, MPI_INT, &num, 1, MPI_INT, 0, MPI_COMM_WORLD);
Но это дает ошибку:
[JM:2861] *** An error occurred in MPI_Scatter
[JM:2861] *** on communicator MPI_COMM_WORLD
[JM:2861] *** MPI_ERR_TRUNCATE: message truncated
[JM:2861] *** MPI_ERRORS_ARE_FATAL: your MPI job will now abort
Однако мне интересно, зачем мне нужно различать корневые и дочерние процессы? Похоже, в этом случае источник / root также получит копию? Другое дело, что другие процессы тоже будут разбегаться? Наверное, нет, но почему? Я думал, что все процессы будут запускать этот код, так как он не в типичном, если я вижу в программах MPI?
if (rank == xxx) {
ОБНОВИТЬ
Я заметил, что для запуска, отправки и получения буфер должен быть одинаковой длины … и данные должны быть объявлены следующим образом:
int data[5][5] = { {0}, {5}, {10}, {3}, {4} };
Заметьте, столбцы объявлены как длина 5, но я только инициализировал 1 значение? Что на самом деле здесь происходит? Этот код правильный? Предположим, я хочу, чтобы каждый процесс получал только 1 значение.
sendcount
количество элементов, которые вы хотите отправить каждый процесс, а не количество элементов в буфере отправки. MPI_Scatter
просто возьму sendcount
* [количество процессов в коммуникаторе] элементов из буфера отправки из корневого процесса и его рассредоточение по всем процессам в коммуникаторе.
Таким образом, чтобы отправить 1 элемент каждому из процессов в коммуникаторе (предположим, что есть 5 процессов), установите sendcount
а также recvcount
быть 1.
MPI_Scatter(data, 1, MPI_INT, &num, 1, MPI_INT, 0, MPI_COMM_WORLD);
Существуют ограничения на возможные пары типов данных, и они такие же, как и для операций точка-точка. Карта типов recvtype
должен быть совместим с картой типа sendtype
то есть они должны иметь один и тот же список базовых базовых типов данных. Также приемный буфер должен быть достаточно большим, чтобы содержать полученное сообщение (оно может быть больше, но не меньше). В большинстве простых случаев тип данных на отправляющей и получающей сторонах одинаков. Так sendcount
— recvcount
пара и sendtype
— recvtype
Пара обычно заканчивается тем же. Пример, в котором они могут различаться, — это когда пользователь использует типы данных с обеих сторон:
MPI_Datatype vec5int;
MPI_Type_contiguous(5, MPI_INT, &vec5int);
MPI_Type_commit(&vec5int);
MPI_Scatter(data, 5, MPI_INT, local_data, 1, vec5int, 0, MPI_COMM_WORLD);
Это работает, так как отправитель создает сообщения из 5 элементов типа MPI_INT
в то время как каждый получатель интерпретирует сообщение как отдельный экземпляр 5-элементного целочисленного вектора.
(Обратите внимание, что вы указываете максимальное количество элементов, которые будут получены в MPI_Recv
и полученная сумма может быть меньше, что может быть получено MPI_Get_count
, Напротив, вы предоставляете ожидаемое количество элементов, которые будут получены в recvcount
из MPI_Scatter
поэтому ошибка будет выдана, если полученная длина сообщения не именно так так же, как и обещал.)
Возможно, вы уже знаете, что распечатка странного ранга вызвана повреждением стека, так как num
может содержать только 1 int
но 5 int
получены в MPI_Scatter
,
Однако мне интересно, зачем мне нужно различать корневые и дочерние процессы? Похоже, в этом случае источник / root также получит копию? Другое дело, что другие процессы тоже будут разбегаться? Наверное, нет, но почему? Я думал, что все процессы будут запускать этот код, так как он не в типичном, если я вижу в программах MPI?
В некоторых операциях, таких как Scatter и Gather, необходимо различать корень и другие процессы в коммуникаторе (они не являются дочерними процессами корня, поскольку они могут находиться на отдельном компьютере), поскольку это коллективное общение (групповое общение), но с одним источником / местом назначения. Поэтому один источник / назначение (нечетный) называется корневым. Всем процессам необходимо знать источник / назначение (корневой процесс), чтобы правильно настроить отправку и получение.
Корневой процесс, в случае Scatter, также получит часть данных (от себя), а в случае Gather также включит свои данные в окончательный результат. Для корневого процесса нет исключений, если не используются операции «на месте». Это также относится ко всем функциям коллективного общения.
Существуют также глобальные коммуникационные операции без корней, такие как MPI_Allgather
где один не обеспечивает корневой ранг. Скорее все ранги получают собираемые данные.
Все процессы в коммуникаторе будут запускать функцию (попробуйте исключить один процесс в коммуникаторе, и вы получите тупик). Вы можете представить процессы на разных компьютерах, выполняющие один и тот же код вслепую. Однако, поскольку каждый из них может принадлежать к разной группе коммуникаторов и иметь разный ранг, функция будет работать по-разному. Каждый процесс знает, является ли он членом коммуникатора, и каждый знает свой ранг и может сравнивать его с рангом корневого процесса (если таковой имеется), чтобы они могли установить связь или выполнить дополнительные действия соответственно.
Других решений пока нет …