Вперед: 4.5.2. Пересылка упакованных данных
Назад: 4.5. Производные типы данных и передача упакованных данных
К содержанию: Оглавление


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

Производные типы MPI не являются в полном смысле типами данных, как это понимается в языках программирования. Они не могут использоваться ни в каких других операциях, кроме коммуникационных. Производные типы MPI следует понимать как описатели расположения в памяти элементов базовых типов. Производный тип MPI представляет собой скрытый (opaque) объект, который специфицирует две вещи: последовательность базовых типов и последовательность смещений. Последовательность таких пар определяется как отображение (карта) типа:

Typemap = {(type0, disp0), ... , (typen-1, dispn-1)}

Значения смещений не обязательно должны быть неотрицательными, различными и упорядоченными по возрастанию. Отображение типа вместе с базовым адресом начала расположения данных buf определяет коммуникационный буфер обмена. Этот буфер будет содержать n элементов, а i-й элемент будет иметь адрес buf+disp и иметь базовый тип type. Стандартные типы MPI имеют предопределенные отображения типов. Например, MPI_INT имеет отображение {(int,0)}.

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

Стандартный сценарий определения и использования производных типов включает следующие шаги:

Любой тип данных в MPI имеет две характеристики: протяженность и размер, выраженные в байтах:

Для простых типов протяженность и размер совпадают.

Функция MPI_Type_extent определяет протяженность элемента некоторого типа.

C:
int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent)
FORTRAN:
MPI_TYPE_EXTENT(DATATYPE, EXTENT, IERROR)
INTEGER DATATYPE, EXTENT, IERROR
IN datatype - тип данных;
OUT extent - протяженность элемента заданного типа.

Функция MPI_Type_size определяет "чистый" размер элемента некоторого типа (за вычетом пустых промежутков).

C:
int MPI_Type_size(MPI_Datatype datatype, int *size)
FORTRAN:
MPI_TYPE_SIZE(DATATYPE, SIZE, IERROR)
INTEGER DATATYPE, SIZE, IERROR
IN datatype - тип данных;
OUT size - размер элемента заданного типа.

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

Самый простой конструктор типа MPI_Type_contiguous создает новый тип, элементы которого состоят из указанного числа элементов базового типа, занимающих смежные области памяти.

C:
int MPI_Type_contiguous(int count, MPI_Datatype oldtype,
MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_CONTIGUOUS(COUNT, OLDTYPE, NEWTYPE, IERROR)
INTEGER COUNT, OLDTYPE, NEWTYPE, IERROR
IN count - число элементов базового типа;
IN oldtype - базовый тип данных;
OUT newtype - новый производный тип данных.

Графическая интерпретация работы конструктора MPI_Type_contiguousа приведена на рис. 4.12.

Графическая интерпретация операции MPI_Type_contiguousа

Рис. 4.12 Графическая интерпретация операции MPI_Type_contiguousа.

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

C:
int MPI_Type_vector(int count, int blocklength, int stride,
MPI_Datatype oldtype, MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_VECTOR(COUNT, BLOCKLENGTH, STRIDE, OLDTYPE,
NEWTYPE, IERROR)
INTEGER COUNT, BLOCKLENGTH, STRIDE, OLDTYPE, NEWTYPE, IERROR
IN count - число блоков;
IN blocklength - число элементов базового типа в каждом блоке;
IN stride - шаг между началами соседних блоков, измеренный числом элементов базового типа;
IN oldtype - базовый тип данных;
OUT newtype - новый производный тип данных.

Функция создает тип newtype, элемент которого состоит из count блоков, каждый из которых содержит одинаковое число blocklength элементов типа oldtype. Шаг stride между началом блока и началом следующего блока всюду одинаков и кратен протяженности представления базового типа. Графическая интерпретация работы конструктора MPI_Type_vector приведена на рис. 4.13.

Графическая интерпретация операции MPI_Type_vector

Рис. 4.13 Графическая интерпретация операции MPI_Type_vector.

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

C:
int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride,
MPI_Datatype oldtype, MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_HVECTOR(COUNT, BLOCKLENGTH, STRIDE, OLDTYPE,
NEWTYPE, IERROR)
INTEGER COUNT, BLOCKLENGTH, STRIDE, OLDTYPE, NEWTYPE, IERROR
IN count - число блоков;
IN blocklength - число элементов базового типа в каждом блоке;
IN stride - шаг между началами соседних блоков в байтах;
IN oldtype - базовый тип данных;
OUT newtype - новый производный тип данных.

Графическая интерпретация работы конструктора MPI_Type_hvector приведена на рис. 4.14.

Графическая интерпретация операции MPI_Type_hvector

Рис. 4.14 Графическая интерпретация операции MPI_Type_hvector.

Конструктор типа MPI_Type_indexed является более универсальным конструктором по сравнению с MPI_Type_vector, так как элементы создаваемого типа состоят из произвольных по длине блоков с произвольным смещением блоков от начала размещения элемента. Смещения измеряются в элементах старого типа.

C:
int MPI_Type_indexed(int count, int *array_of_blocklengths,
int *array_of_displacements, MPI_Datatype oldtype,
MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_INDEXED(COUNT, ARRAY_OF_BLOCKLENGTHS,
ARRAY_OF_DISPLACEMENTS, OLDTYPE, NEWTYPE, IERROR)
INTEGER COUNT, ARRAY_OF_BLOCKLENGTHS(*), ARRAY_OF_DISPLACEMENTS(*),
OLDTYPE, NEWTYPE, IERROR
IN count - число блоков;
IN array_of_blocklengths - массив, содержащий число элементов базового типа в каждом блоке;
IN array_of_displacements - массив смещений каждого блока от начала размещения элемента нового типа, смещения измеряются числом элементов базового типа;
IN oldtype - базовый тип данных;
OUT newtype - новый производный тип данных.

Эта функция создает тип newtype, каждый элемент которого состоит из count блоков, где i-ый блок содержит array_of_blocklengths[i] элементов базового типа и смещен от начала размещения элемента нового типа на array_of_displacements[i] элементов базового типа. Графическая интерпретация работы конструктора MPI_Type_indexedа приведена на рис. 4.15.

Графическая интерпретация операции MPI_Type_indexedа

Рис. 4.15 Графическая интерпретация операции MPI_Type_indexedа.

Конструктор типа MPI_Type_hindexed идентичен конструктору MPI_Type_indexed за исключением того, что смещения измеряются в байтах.

C:
int MPI_Type_hindexed(int count, int *array_of_blocklengths,
MPI_Aint *array_of_displacements, MPI_Datatype oldtype,
MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_HINDEXED(COUNT, ARRAY_OF_BLOCKLENGTHS,
ARRAY_OF_DISPLACEMENTS, OLDTYPE, NEWTYPE, IERROR)
INTEGER COUNT, ARRAY_OF_BLOCKLENGTHS(*), ARRAY_OF_DISPLACEMENTS(*),
OLDTYPE, NEWTYPE, IERROR
IN count - число блоков;
IN array_of_blocklengths - массив, содержащий число элементов базового типа в каждом блоке;
IN array_of_displacements - массив смещений каждого блока от начала размещения элемента нового типа, смещения измеряются в байтах;
IN oldtype - базовый тип данных;
OUT newtype - новый производный тип данных.

Элемент нового типа состоит из count блоков, где i-ый блок содержит array_of_blocklengths[i] элементов старого типа и смещен от начала размещения элемента нового типа на array_of_displacements[i] байт. Графическая интерпретация работы конструктора MPI_Type_hindexed приведена на рис. 4.16.

Графическая интерпретация операции MPI_Type_hindexed

Рис. 4.16 Графическая интерпретация операции MPI_Type_hindexed.

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

C:
int MPI_Type_struct(int count, int *array_of_blocklengths,
MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types,
MPI_Datatype *newtype)
FORTRAN:
MPI_TYPE_STRUCT(COUNT, ARRAY_OF_BLOCKLENGTHS,
ARRAY_OF_DISPLACEMENTS, ARRAY_OF_TYPES, NEWTYPE, IERROR)
INTEGER COUNT, ARRAY_OF_BLOCKLENGTHS(*), ARRAY_OF_DISPLACEMENTS(*),
ARRAY_OF_TYPES(*), NEWTYPE, IERROR
IN count - число блоков;
IN array_of_blocklength - массив, содержащий число элементов одного из базовых типов в каждом блоке;
IN array_of_displacements - массив смещений каждого блока от начала размещения структуры, смещения измеряются в байтах;
IN array_of_type - массив, содержащий тип элементов в каждом блоке;
OUT newtype - новый производный тип данных.

Функция создает тип newtype, элемент которого состоит из count блоков, где i-ый блок содержит array_of_blocklengths[i] элементов типа array_of_types[i]. Смещение i-ого блока от начала размещения элемента нового типа измеряется в байтах и задается в array_of_displacements[i].

Графическая интерпретация работы конструктора MPI_Type_struct приведена на рис. 4.17.

Графическая интерпретация операции MPI_Type_struct

Рис. 4.17 Графическая интерпретация операции MPI_Type_struct.

Функция MPI_Type_commit регистрирует созданный производный тип. Только после регистрации новый тип может использоваться в коммуникационных операциях.

C:
int MPI_Type_commit(MPI_Datatype *datatype)
FORTRAN:
MPI_TYPE_COMMIT(DATATYPE, IERROR)
INTEGER DATATYPE, IERROR
INOUT datatype - новый производный тип данных.

Функция MPI_Type_free уничтожает описатель производного типа.

C:
int MPI_Type_free(MPI_Datatype *datatype)
FORTRAN:
>
MPI_TYPE_FREE(DATATYPE, IERROR)
INTEGER DATATYPE, IERROR
INOUT datatype - уничтожаемый производный тип данных.

Функция MPI_Type_free устанавливает описатель типа в состояние MPI_DATATYPE_NULL. Это не повлияет на выполняющиеся в данный момент коммуникационные операции с этим типом данных и на производные типы, которые ранее были определены через уничтоженный тип.

Для определения длины сообщения используются две функции: MPI_Get_count и MPI_Get_elements. Для сообщений из простых типов они возвращают одинаковое число. Подпрограмма MPI_Get_count возвращает число элементов типа datatype, указанного в операции получения. Если получено не целое число элементов, то она возвратит константу MPI_UNDEFINED (функция MPI_Get_count рассматривалась в разделе 4.3.2., посвященном коммуникационным операциям типа точка-точка).

Функция MPI_Get_elements возвращает число элементов простых типов, содержащихся в сообщении.

C:
int MPI_Get_elements(MPI_Status *status, MPI_Datatype datatype,
int *count)
FORTRAN:
MPI_GET_ELEMENTS(STATUS, DATATYPE, COUNT, IERROR)
INTEGER STATUS(MPI_STATUS_SIZE), DATATYPE, COUNT, IERROR
IN status - статус сообщения;
IN datatype - тип элементов сообщения;
OUT count - исло элементов простых типов, содержащихся в сообщении.

Приведем пример программы на использование производных типов данных. В программе создается векторный тип данных для пересылки элементов массива, не образующих непрерывное поле в памяти. В программе на Си - это столбцы матрицы, а на Фортране - строки матрицы.

Программа type.c

#include "mpi.h"
#include <stdio.h>
#define SIZE 4

int main(argc,argv)
int argc;
char *argv[]; {
int numtasks, rank, source=0, dest, tag=1, i;
float a[SIZE][SIZE] = 
 {1.0, 2.0, 3.0, 4.0, 
  5.0, 6.0, 7.0, 8.0, 
  9.0, 10.0, 11.0, 12.0,
 13.0, 14.0, 15.0, 16.0};
float b[SIZE]; 

MPI_Status stat;
MPI_Datatype columntype;

MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
  
MPI_Type_vector(SIZE, 1, SIZE, MPI_FLOAT, &columntype);
MPI_Type_commit(&columntype);

if (numtasks == SIZE) {
 if (rank == 0) {
   for (i=0; i<numtasks; i++) 
 MPI_Send(&a[0][i], 1, columntype, i, tag, MPI_COMM_WORLD);
    }
MPI_Recv(b,SIZE,MPI_FLOAT,source,tag,MPI_COMM_WORLD, &stat);
 printf("rank= %d b= %3.1f %3.1f %3.1f %3.1f\n",
    rank,b[0],b[1],b[2],b[3]);
 }
else
 printf("Must specify %d processors. Terminating.\n",SIZE);
  
MPI_Type_free(&columntype);
MPI_Finalize();
}

Программа type.f

program vector
  include 'mpif.h'

  integer SIZE
  parameter(SIZE=4)
  integer numtasks, rank, source, dest, tag, i, ierr
  real*4 a(0:SIZE-1,0:SIZE-1), b(0:SIZE-1)
  integer stat(MPI_STATUS_SIZE), rowtype

C Fortran stores this array in column major order
  data a /1.0, 2.0, 3.0, 4.0, 
 &     5.0, 6.0, 7.0, 8.0,
 &     9.0, 10.0, 11.0, 12.0, 
 &     13.0, 14.0, 15.0, 16.0 /

  call MPI_INIT(ierr)
  call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, numtasks, ierr)
  call MPI_TYPE_VECTOR(SIZE,1,SIZE,MPI_REAL,rowtype,ierr)
  call MPI_TYPE_COMMIT(rowtype, ierr)
 
  tag = 1
  if (numtasks .eq. SIZE) then
   if (rank .eq. 0) then
     do 10 i=0, numtasks-1
     call MPI_SEND(a(i,0), 1, rowtype, i, tag,
 &          MPI_COMM_WORLD, ierr)
 10   continue
   endif
   source = 0
   call MPI_RECV(b, SIZE, MPI_REAL, source, tag, 
 &        MPI_COMM_WORLD, stat, ierr)
   print *, 'rank= ',rank,' b= ',b

  else
  print *, 'Must specify',SIZE,' processors. Terminating.' 
  endif

  call MPI_TYPE_FREE(rowtype, ierr)
  call MPI_FINALIZE(ierr)

  end

Output: 
rank= 0 b= 1.0 5.0 9.0 13.0
rank= 1 b= 2.0 6.0 10.0 14.0
rank= 2 b= 3.0 7.0 11.0 15.0
rank= 3 b= 4.0 8.0 12.0 16.0


Вперед: 4.5.2. Пересылка упакованных данных
Назад: 4.5. Производные типы данных и передача упакованных данных
К содержанию: Оглавление