Типы данных, производные от Stacked MPI, в Fortran

MPI2 позволяет нам создавать производные типы данных и отправлять их, записывая

call mpi_type_create_indexed_block(size,1,dspl_send,rtype,DerType,ierr)
call mpi_send(data,1,DerType,jRank,20,comm,ierr)

Делая это, позиция dspl_send data(N) отправляется библиотекой MPI.

Теперь для матрицы data(M,N) мы можем отправить ее позицию с помощью следующего кода:

call mpi_type_create_indexed_block(size,M,dspl_send,rtype,DerTypeM,ierr)
call mpi_send(data,1,DerTypeM,jRank,20,comm,ierr)

То есть отправляются записи data(i, dspl_send(j)).

Мой вопрос касается роли 1 в последующем mpi_send. Всегда ли должно быть 1? Возможен ли другой размер? Типы данных, производные от MPI, хорошо объясняются во многих документах в Интернете, но размер в send/recv всегда равен 1 без упоминания о том, разрешен ли другой размер и как его можно использовать.

Если мы хотим работать с матрицами data(M,N) с размером M, который меняется между вызовами, нужно ли нам всегда создавать производный тип данных всякий раз, когда мы его вызываем? Нельзя ли использовать DerType для отправки матрицы data(M,N) или data(N,M)?


person Mathieu Dutour Sikiric    schedule 10.12.2012    source источник


Ответы (2)


Каждый тип данных MPI имеет два свойства: размер и экстент. Размер — это фактическое количество байтов, которые представляет тип данных, а экстент — это количество байтов, которые тип данных занимает в памяти. Некоторые типы данных не являются смежными, что означает, что их размер может быть меньше, чем их экстент, например. (показано здесь в псевдокоде)

MPI_TYPE_VECTOR(count = 1,
                blocklength = 10,
                stride = 20,
                oldtype = MPI_INTEGER,
                newtype = newtype)

создает тип данных, который берет первые 10 (blocklength) элементов из 20 (stride). Этот тип данных имеет размер, в 10 раз превышающий размер MPI_INTEGER, что в большинстве систем составляет 40 байт. Его размер в два раза больше или 80 байт на большинстве систем. Если бы count было равно 2, то он занял бы 10 элементов, затем пропустил бы следующие 10, затем взял бы еще 10 элементов и еще раз пропустил бы следующие 10. Следовательно, его размер и его протяженность были бы в два раза больше.

Когда вы указываете определенное количество элементов в любой подпрограмме MPI, например. MPI_SEND, MPI делает что-то вроде этого:

  1. Он инициализирует внутренний буфер данных адресом аргумента исходного буфера.
  2. Он обращается к карте типов данных, чтобы решить, сколько байтов и откуда взять, и добавляет их к создаваемому сообщению. Количество добавляемых байтов равно размеру типа данных.
  3. Он увеличивает внутренний указатель данных на степень типа данных.
  4. Он уменьшает внутренний счетчик и, если он все еще не равен нулю, повторяет предыдущие два шага.

Одной из отличных особенностей MPI является то, что экстент типа данных не обязательно должен соответствовать его размеру (как показано в векторном примере), и можно даже присвоить любое значение экстента, которое он хочет, для типа данных, используя MPI_TYPE_CREATE_RESIZED. Это позволяет создавать очень сложные шаблоны доступа к данным. Например, использование MPI_SCATTERV для разброса матрицы по блокам, которые не охватывают целые строки (C) или столбцы (Fortran), требует использования таких типов с измененным размером.

Вернемся к векторному примеру. Независимо от того, создаете ли вы векторный тип с помощью count = 1, а затем вызываете MPI_SEND с помощью count = 2, или создаете векторный тип с помощью count = 2, а затем вызываете MPI_SEND с помощью count = 1, конечный результат будет одинаковым. Часто создается тип данных, полностью описывающий объект, который требуется отправить. В этом случае один дает count = 1 в вызове MPI_SEND. Но бывают случаи, когда может быть выгоднее создать тип данных, описывающий только часть объекта, например одну часть, а затем вызвать MPI_SEND с count, равным количеству частей, которые нужно отправить. Иногда это вопрос личных предпочтений, иногда — алгоритмических требований.

Что касается вашего последнего вопроса, Фортран хранит матрицы в порядке столбцов, что означает, что data(i,j) находится рядом с data(i±1,j) в памяти, а не с data(i,j±1). Следовательно, data(M,N) состоит из N последовательных векторов-столбцов по M элементов каждый. Расстояние между двумя элементами, например data(1,1) и data(1,2), зависит от M. Вот почему вы предоставляете M в конструкторе типа. Матрицы с разным количеством строк (например, разные M) не будут «подходить» к карте типов созданного типа, и для построения сообщения будут использоваться неправильные элементы.

person Hristo Iliev    schedule 10.12.2012
comment
Спасибо, это прояснило некоторые вопросы. Однако у меня все еще есть странности с openmpi - person Mathieu Dutour Sikiric; 20.12.2012

Описание экстента в https://stackoverflow.com/a/13802243/7784768 не совсем корректно, так как экстент не учитывает заполнение в конце типа данных. Типы данных MPI определяются картой типов:

typemap = ((type_0, disp_0 ), ..., (type_n−1, disp_n−1 ))

Затем степень определяется в соответствии с

lb = min(disp_j)
ub = max(disp_j + sizeof(type_j)) + e)
extent = ub - lb,

где e может быть ненулевым из-за требований выравнивания.

Это означает, что в примере

MPI_TYPE_VECTOR(count = 1,
                blocklength = 10,
                stride = 20,
                oldtype = MPI_INTEGER,
                newtype = newtype)

с count=1, typemap

((int, 0), (int, 4), ... (int, 36))

и экстент в большинстве систем равен 40, а не 80 (т. е. в этом случае шаг не влияет на карту типов). Для count=2 typemap будет

((int, 0), (int, 4), ... (int, 36), (int, 80), (int, 84), ... (int, 116))

и экстент 120 (40 байтов для первого блока из 10 целых чисел, 40 байтов для шага и 40 байтов для второго блока из 10 целых чисел, но оставшийся шаг не учитывается в экстенте). Можно легко узнать экстент с помощью функции MPI_Type_get_extent.

Экстент — довольно сложная концепция, и легко допустить ошибку, пытаясь передать несколько элементов производного типа данных.

person Jussi Enkovaara    schedule 29.03.2017
comment
Добро пожаловать. Не используйте формулировки, подобные приведенным выше, потому что порядок ответов часто меняется. Теперь ваш ответ наверху. - person Vladimir F; 29.03.2017