Ответ 1
Вот длинное описание этой проблемы в моем ответе на этот вопрос: тот факт, что многие люди эти вопросы являются доказательством того, что это не очевидно, и идеи привыкают.
Важно знать, какой макет памяти описывает тип данных MPI. Вызывающая последовательность до MPI_Type_vector
:
int MPI_Type_vector(int count,
int blocklength,
int stride,
MPI_Datatype old_type,
MPI_Datatype *newtype_p)
Создает новый тип, который описывает макет памяти, где находится каждый элемент stride
, есть блок из blocklength
выведенных элементов и всего count
этих блоков. Элементы здесь находятся в единицах того, что было old_type
. Так, например, если вы позвонили (назвав здесь параметры, которые вы не можете сделать на C, но:)
MPI_Type_vector(count=3, blocklength=2, stride=5, old_type=MPI_INT, &newtype);
Тогда newtype
будет описывать макет в памяти следующим образом:
|<----->| block length
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | X | | | | X | X | | | | X | X | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|<---- stride ----->|
count = 3
где каждый квадрат является одним целым размером памяти, предположительно 4 байта. Обратите внимание, что шаг - это расстояние в целых числах от начала одного блока до начала следующего, а не расстояние между блоками.
Хорошо, поэтому в вашем случае вы вызвали
MPI_Type_vector(N, 1, N, MPI_DOUBLE, &col);
который будет принимать блоки count = N
, каждый из которых имеет размер blocklength=1
MPI_DOUBLE
s, с пробелом между началом каждого блока stride=N
MPI_DOUBLE
s. Другими словами, это займет каждый N'th double, всего N раз; идеально подходит для извлечения одного столбца из (смежно сохраненного) массива NxN двойников. Удобная проверка заключается в том, чтобы увидеть, сколько данных перечеркивается (count*stride = N*N
, который является полным размером матрицы, проверка) и сколько данных фактически включено (count*blocksize = N
, который является размером столбца, проверьте. )
Если все, что вам нужно было сделать, это вызвать MPI_Send и MPI_Recv для обмена отдельными столбцами, вы бы сделали это; вы можете использовать этот тип, чтобы описать расположение столбца, и все будет в порядке. Но есть еще одна вещь.
Вы хотите вызвать MPI_Scatter
, который отправляет первый coltype (скажем) на процессор 0, следующий coltype на процессор 1 и т.д. Если вы делаете это с помощью простого массива 1d, легко понять, где "следующий" тип данных; если вы рассеиваете 1 int на каждый процессор, "следующий" int начинается сразу после завершения первого int.
Но ваш новый столбец coltype имеет общую степень, которая начинается с начала столбца до N*N
MPI_DOUBLE
позже - если MPI_Scatter следует той же логике (он делает), он начнет искать "следующий" столбец вне памяти матриц целиком и так далее со следующим и последующим. Мало того, что вы не получите ответ, который вам нужен, программа, скорее всего, потерпит крах.
Способ исправить это, чтобы сообщить MPI, что "размер" этого типа данных для целей расчета, где находится "следующий", - это размер в памяти между тем, где начинается один столбец, и начинается следующий столбец; то есть ровно один MPI_DOUBLE
. Это не влияет на количество отправленных данных, которое по-прежнему составляет 1 столбцов данных; это влияет только на вычисление "следующий в строке". С столбцами (или строками) в массиве вы можете просто отправить этот размер в соответствующий размер шага в памяти, а MPI выберет правильный следующий столбец для отправки. Без этого оператора изменения ваша программа, скорее всего, выйдет из строя.
Если у вас более сложные макеты данных, например, в 2d-блоках примера 2d-массива, связанного выше, то между "ближайшими" элементами нет ни одного размера шага; вам все равно нужно сделать трюк для изменения размера, чтобы размер был полезной единицей, но тогда вам нужно использовать MPI_Scatterv, а не разбрасывать явно укажите местоположения для отправки.