Попытка передать производные типы MPI между процессорами (и неудача)

Я пытаюсь распараллелить клиентский код Fortran с MPI. f - это массив 4-байтовых вещественных чисел с размерами f(dimx,dimy,dimz,dimf). Мне нужны различные процессы для работы с разными частями первого измерения массива. (Я бы предпочел начать с последнего, но это было не до меня.) Итак, я определяю производный тип mpi_x_inteface следующим образом

call mpi_type_vector(dimy*dimz*dimf, 1, dimx, MPI_REAL,  &
                     mpi_x_interface, mpi_err)
call mpi_type_commit(mpi_x_interface, mpi_err)

Мое намерение состоит в том, чтобы один mpi_x_interface содержал все данные в 'f' с некоторым заданным первым индексом «i». То есть для данного i он должен содержать f(i,:,:,:). (Обратите внимание, что на этом этапе игры у всех процедур есть полная копия f. Я намерен в конечном итоге разделить f между процессами, за исключением того, что я хочу, чтобы у процесса 0 была полная копия для целей сбора.)

ptsinproc - это массив, содержащий количество индексов "i", обрабатываемых каждой процедурой. x_slab_displs - это смещение от начала массива для каждой процедуры. Для двух процессов, которые я тестирую, это ptsinproc=(/61,60/), x_slab_displs=(/0,61/). myminpt - простое целое число, дающее минимальный индекс, обрабатываемый в каждой процедуре.

Итак, теперь я хочу собрать все f в процесс 0, и я запускаю

    if (myrank == 0) then
      call mpi_gatherv(MPI_IN_PLACE, ptsinproc(myrank),
 +                     mpi_x_interface, f(1,1,1,1), ptsinproc,
 +                     x_slab_displs, mpi_x_interface, 0,
 +                     mpi_comm_world, mpi_err)
    else
      call mpi_gatherv(f(myminpt,1,1,1), ptsinproc(myrank),
 +                     mpi_x_interface, f(1,1,1,1), ptsinproc,
 +                     x_slab_displs, mpi_x_interface, 0,
 +                     mpi_comm_world, mpi_err)
    endif

Я могу отправить не более одной такой "плиты". Если я попытаюсь отправить все 60 «блоков» из процесса 1 в процесс 0, я получу ошибку сегмента из-за «недопустимой ссылки на память». Кстати, даже когда я отправляю этот единственный кусок, данные оказываются не в том месте.

Я проверил все очевидные вещи, такие как maiking, и убедитесь, что myrank, ptsinproc и x_slab_dislps - это то, что они должны быть во всех процессах. Я изучал разницу между «размером» и «протяженностью» и так далее, но безрезультатно. Я на грани своего остроумия. Я просто не понимаю, что делаю неправильно. И кто-то может вспомнить, что я задал похожий (но другой!) Вопрос несколько месяцев назад. Признаюсь, я просто не понимаю. Мы ценим ваше терпение.


person bob.sacamento    schedule 08.02.2016    source источник
comment
Исправлен старый код, над которым я работаю. Бесплатная форма - это новый файл, который я создал сам.   -  person bob.sacamento    schedule 08.02.2016
comment
Если вы используете OPENMPI реализацию MPI, убедитесь, что у вас установлена ​​последняя возможная версия или, по крайней мере, версия выше, чем 1.8.5. Было много проблем с MPI_IN_PLACE в OPENMPI. Вы можете проверить open-mpi.org/source/new.php и выполнить поиск MPI_IN_PLACE, чтобы узнать, сколько раз исправлялись проблемы, связанные с MPI_IN_PLACE. Простая проверка будет заключаться в замене MPI_IN_PLACE фактическим буфером.   -  person innoSPG    schedule 08.02.2016
comment
@innoSPG Когда я делаю замену, я получаю ошибку с другой ошибкой: буферы не должны иметь псевдонима. Это что-то показывает? Но на самом деле я использую MPICH.   -  person bob.sacamento    schedule 09.02.2016
comment
Если вы уже используете MPICH, проблема в другом. Возможно, в сопоставлении типов данных. Я не являюсь большим пользователем пользовательского типа данных MPI. Что я обычно делаю в такой ситуации, так это переделываю массив, чтобы он имел правильный порядок. Конечно, я делаю это, когда память не является большой проблемой. Потому что в какой-то момент времени требуется две копии большого массива.   -  person innoSPG    schedule 09.02.2016
comment
Я знаю, что он не отвечает на ваш вопрос, но вы действительно не должны называть свои собственные функции и переменные, начинающиеся с mpi_.   -  person Hristo Iliev    schedule 09.02.2016


Ответы (1)


Во-первых, я просто хочу сказать, что причина, по которой вы сталкиваетесь с таким количеством проблем, заключается в том, что вы пытаетесь разделить первую (самую быструю) ось. Это вообще не рекомендуется, потому что для упаковки вашего mpi_x_interface "как есть" требуется много несмежных обращений к памяти. Речь идет об огромной потере производительности.

Разделение самой медленной оси между процессами MPI - гораздо лучшая стратегия. Я настоятельно рекомендую транспонировать вашу 4-мерную матрицу так, чтобы ось x была последней, если вы можете.

Теперь к вашей актуальной проблеме (-ам) ...

Производные типы данных

Как вы уже поняли, одна из проблем заключается в том, что размер и экстент вашего производного типа данных могут быть неправильными. Давайте немного упростим вашу задачу, чтобы я мог нарисовать картинку. Скажите dimy*dimz*dimf=3 и dimx=4. Как есть, ваш тип данных mpi_x_interface описывает следующие данные в памяти:

| X |   |   |   | X |   |   |   | X |   |   |   |

То есть каждые 4-е MPI_REAL, а всего их 3. Поскольку это то, что вы хотите, пока все хорошо: размер вашей переменной правильный. Однако, если вы попытаетесь отправить «следующий» mpi_x_interface, вы увидите, что ваша реализация MPI начнется в следующей точке памяти (которая в вашем случае не была выделена) и бросит вам «недопустимый доступ к памяти»:

                                             tries to access and bombs
                                                 vvv
| X |   |   |   | X |   |   |   | X |   |   |   | Y |   |   |   | Y | ...

Что вам нужно сообщить MPI как часть вашего типа данных, так это то, что «следующий» mpi_x_interface запускает только 1 real в массив. Это достигается путем переопределения «степени» производного типа данных путем вызова MPI_Type_create_resized(). В вашем случае нужно написать

integer :: mpi_x_interface, mpi_x_interface_resized
integer, parameter :: SIZEOF_REAL = 4 ! or whatever f actually is

call mpi_type_vector(dimy*dimz*dimf, 1, dimx, MPI_REAL,  &
                 mpi_x_interface, mpi_err)
call mpi_type_create_resized(mpi_x_interface, 0, 1*SIZEOF_REAL, &
                             mpi_x_interface_resized, mpi_err)
call mpi_type_commit(mpi_x_interface_resized, mpi_err)

Тогда вызов "следующего" 3 mpi_x_interface_resized приведет к:

| X | Y | Z | A | X | Y | Z | A | X | Y | Z | A |

как и ожидалось.

MPI_Gatherv

Обратите внимание, что теперь вы правильно определили экстент своего типа данных, вызов mpi_gatherv со смещением в терминах вашего типа данных теперь должен работать должным образом.

Лично я бы не подумал, что есть необходимость пробовать какую-то причудливую логику с MPI_IN_PLACE для коллективной операции. Вы можете просто установить myminpt=1 на myrank==0. Тогда вы можете называть каждое звание:

   call mpi_gatherv(f(myminpt,1,1,1), ptsinproc(myrank),
+                     mpi_x_interface_resized, f, ptsinproc,
+                     x_slab_displs, mpi_x_interface_resized, 0,
+                     mpi_comm_world, mpi_err)
person NoseKnowsAll    schedule 08.02.2016
comment
... причина, по которой вы сталкиваетесь с таким количеством проблем, заключается в том, что вы пытаетесь разделить первую (самую быструю) ось. Вы совершенно правы, но, как я уже сказал, это не мое решение. Спасибо за Ваш ответ. Смотрю. - person bob.sacamento; 09.02.2016
comment
Это сработало! Спасибо! Таким образом, идея заключается в том, что MPI будет думать, что местоположение следующего отправляемого элемента - это ячейка памяти прямо в конце первого элемента (игнорируя такие сложности, как заполнение). Я хотел, чтобы MPI построил второй элемент, начав 4 байта после начала моего первого элемента, но мне пришлось объяснить это MPI. Звучит правильно? Спасибо еще раз. - person bob.sacamento; 09.02.2016
comment
@ bob.sacamento Да, именно так. Экстент производного типа данных по умолчанию устанавливается равным размеру производного типа данных. В вашем случае вы хотели изменить экстент только на 4 байта, но оставить размер прежним. - person NoseKnowsAll; 09.02.2016