Опубликован: 28.07.2007 | Доступ: свободный | Студентов: 2026 / 503 | Оценка: 4.53 / 4.26 | Длительность: 25:10:00
ISBN: 978-5-9556-0096-3
Специальности: Программист
Лекция 5:

Параллельное программирование на основе MPI

5.5.2.3. Индексный способ конструирования

При индексном способе конструирования производного типа данных в MPI используются функции:

int MPI_Type_indexed(int count, int blocklens[], int indices[], 
 MPI_Data_type oldtype, MPI_Datatype *newtype) и
int MPI_Type_hindexed(int count, int blocklens[], MPI_Aint indices[], 
 MPI_Data_type oldtype, MPI_Datatype *newtype),

где

  • count — количество блоков;
  • blocklens — количество элементов в каждом блоке;
  • indices — смещение каждого блока от начала типа;
  • oldtype — исходный тип данных;
  • newtype — новый определяемый тип данных.

Как следует из описания, при индексном способе новый производный тип создается как набор блоков разного размера из элементов исходного типа, при этом между блоками могут иметься разные промежутки по памяти. Для пояснения данного способа можно привести пример конструирования типа для описания верхней треугольной матрицы размером nxn:

// Конструирование типа для описания верхней треугольной матрицы
for ( i = 0, i < n; i++ ) {
 blocklens[i] = n - i;
 indices  [i] = i * n + i;
}
MPI_Type_indexed(n, blocklens, indices, &UTMatrixType, &ElemType).

Как и ранее, способ конструирования, определяемый функцией MPI_Type_hindexed, отличается тем, что элементы indices для определения интервалов между блоками задаются в байтах, а не в элементах исходного типа данных.

Следует отметить, что существует еще одна дополнительная функция MPI_Type_create_indexed_block индексного способа конструирования для определения типов с блоками одинакового размера (данная функция предусматривается стандартом MPI -2).

5.5.2.4. Структурный способ конструирования

Как отмечалось ранее, структурный способ является самым общим методом конструирования производного типа данных при явном задании соответствующей карты типа. Использование такого способа производится при помощи функции:

int MPI_Type_struct(int count, int blocklens[], MPI_Aint indices[], 
 MPI_Data_type oldtypes[], MPI_Datatype *newtype),

где

  • count — количество блоков;
  • blocklens — количество элементов в каждом блоке;
  • indices — смещение каждого блока от начала типа (в байтах);
  • oldtypes — исходные типы данных в каждом блоке в отдельности;
  • newtype — новый определяемый тип данных.

Как следует из описания, структурный способ дополнительно к индексному методу позволяет указывать типы элементов для каждого блока в отдельности.

5.5.3. Объявление производных типов и их удаление

Рассмотренные в предыдущем пункте функции конструирования позволяют определить производный тип данных. Дополнительно перед использованием созданный тип должен быть объявлен при помощи функции:

int MPI_Type_commit(MPI_Datatype *type),

где

  • type – объявляемый тип данных.

При завершении использования производный тип должен быть аннулирован при помощи функции:

int MPI_Type_free(MPI_Datatype *type),

где

  • type – аннулируемый тип данных.
5.5.4. Формирование сообщений при помощи упаковки и распаковки данных

Наряду с рассмотренными в п. 5.5.2 методами конструирования производных типов в MPI предусмотрен и явный способ сборки и разборки сообщений, содержащих значения разных типов и располагаемых в разных областях памяти.

Для использования данного подхода должен быть определен буфер памяти достаточного размера для сборки сообщения. Входящие в состав сообщения данные должны быть упакованы в буфер при помощи функции:

int MPI_Pack(void *data, int count, MPI_Datatype type, void *buf,
  int bufsize, int *bufpos, MPI_Comm comm),

где

  • data — буфер памяти с элементами для упаковки;
  • count — количество элементов в буфере;
  • type — тип данных для упаковываемых элементов;
  • buf — буфер памяти для упаковки;
  • bufsize — размер буфера в байтах;
  • bufpos — позиция для начала записи в буфер (в байтах от начала буфера);
  • comm — коммуникатор для упакованного сообщения.

Функция MPI_Pack упаковывает count элементов из буфера data в буфер упаковки buf, начиная с позиции bufpos. Общая схема процедуры упаковки показана на рис. 5.8а.

Общая схема упаковки и распаковки данных

Рис. 5.8. Общая схема упаковки и распаковки данных

Начальное значение переменной bufpos должно быть сформировано до начала упаковки и далее устанавливается функцией MPI_Pack. Вызов функции MPI_Pack осуществляется последовательно для упаковки всех необходимых данных. Так, в ранее рассмотренном примере набора переменных a, b и n для их упаковки необходимо выполнить:

bufpos = 0;
MPI_Pack(&a, 1, MPI_DOUBLE,	buf, buflen, &bufpos, comm);
MPI_Pack(&b, 1, MPI_DOUBLE,	buf, buflen, &bufpos, comm);
MPI_Pack(&n, 1, MPI_INT,  	buf, buflen, &bufpos, comm);

Для определения необходимого размера буфера для упаковки может быть применена функция:

int MPI_Pack_size(int count, MPI_Datatype type, MPI_Comm comm, int *size),

где

  • count — количество элементов в буфере;
  • type — тип данных для упаковываемых элементов;
  • comm — коммуникатор для упакованного сообщения;
  • size — рассчитаный размер буфера.

После упаковки всех необходимых данных подготовленный буфер может быть использован в функциях передачи данных с указанием типа MPI_PACKED.

После получения сообщения с типом MPI_PACKED данные могут быть распакованы при помощи функции:

int MPI_Unpack(void *buf, int bufsize, int *bufpos, void *data, 
  int count, MPI_Datatype type, MPI_Comm comm),

где

  • buf — буфер памяти с упакованными данными;
  • bufsize — размер буфера в байтах;
  • bufpos — позиция начала данных в буфере (в байтах от начала буфера);
  • data — буфер памяти для распаковываемых данных;
  • count — количество элементов в буфере;
  • type — тип распаковываемых данных;
  • comm — коммуникатор для упакованного сообщения.

Функция MPI_Unpack распаковывает, начиная с позиции bufpos, очередную порцию данных из буфера buf и помещает распакованные данные в буфер data. Общая схема процедуры распаковки показана на рис. 5.8б.

Начальное значение переменной bufpos должно быть сформировано до начала распаковки и далее устанавливается функцией MPI_Unpack. Вызов функции MPI_Unpack осуществляется последовательно для распаковки всех упакованных данных, при этом порядок распаковки должен соответствовать порядку упаковки. Так, в ранее рассмотренном примере упаковки для распаковки упакованных данных необходимо выполнить:

bufpos = 0;
MPI_Unpack(buf, buflen, &bufpos, &a, 1, MPI_DOUBLE, comm);
MPI_Unpack(buf, buflen, &bufpos, &b, 1, MPI_DOUBLE, comm);
MPI_Unpack(buf, buflen, &bufpos, &n, 1, MPI_INT,    comm);

В заключение выскажем ряд рекомендаций по применению упаковки для формирования сообщений. Поскольку такой подход приводит к появлению дополнительных действий по упаковке и распаковке данных, то данный способ может быть оправдан при сравнительно небольших размерах сообщений и при малом количестве повторений. Упаковка и распаковка может оказаться полезной при явном использовании буферов для буферизованного способа передачи данных.