Лекция 4. Производные типы
данных в MPI
Пазников Алексей Александрович
Параллельные вычислительные технологии
СибГУТИ (Новосибирск)
Осенний семестр 2015
www: cpct.sibsutis.ru/~apaznikov/teaching
q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall
Производные типы
данных
3
Производные типы данных (derived datatypes)
Сообщения содержат некоторое число элементов некоторого типа данных.
В MPI типы бывают
▪ Базовые (basic)
▪ Производные (derived), которые могут быт получены из базовых
и производных типов.
Примеры сообщений в MPI
Соответствия между базовыми типами MPI и типами С:
4
Производные типы данных (derived datatypes)
struct buff_s {
int ival[3];
double dval[5];
} buffer;
Компилятор
int double
MPI_Send(&buffer, 1, buff_datatype, ...)
&buffer = начальный
адрес данных
Указатель,
описывающий
смещение
array_of_types[0]=MPI_INT;
array_of_blocklengths[0]=3;
array_of_displacements[0]=0;
array_of_types[1]=MPI_DOUBLE;
array_of_blocklengths[1]=5;
array_of_displacements[1]=...;
MPI_Type_struct(2, array_of_blocklengths,
array_of_displacements, array_of_types,
&buff_datatype );
MPI_Type_commit(&buff_datatype);
5
Производные типы данных (derived datatypes)
базовый тип 0
Производные типы данных представляют собой список указателей на базовые
типы данных.
базовый тип 1
...
базовый тип n – 1
смещение для типа 0
смещение для типа 1
...
смещение для типа n – 1
c 11 22 2.343546e35Пример:
0 4 8 12 16 20 24
MPI_CHAR
MPI_INT
MPI_INT
MPI_DOUBLE
0
4
8
16
Производные типы
данных описывают
расположение объектов в
памяти, например, для
структур, блоков,
массивов и т.д.
6
Непрерывные данные
▪ Самый простой вид производных типов.
▪ Состоит из числа непрерывных элементов одного типа
старый тип
новый тип
int MPI_Type_contiguous(int count, MPI_Datatype oldtype,
MPI_Datatype *newtype);
7
Векторный тип данных
blocklength = 3
элемента в блоке
int MPI_Type_vector(int count, int blocklength, int stride,
MPI_Datatype oldtype, MPI_Datatype *newtype);
старый тип
новый тип
stride = 5
(элементов между блоками)
Пустые области памяти также
необходимо передавать
count = 2
(число блоков)
8
Структурный тип данных
блок 1
int MPI_Type_vector(int count, int *array_of_blocklengths,
MPI_Aint *array_of_displacements,
MPI_Datatype *array_of_types,
MPI_Datatype *newtype);
старый тип
новый тип
Пустая область памяти, если double
хранится с 8-байтным смещением
MPI_INT MPI_DOUBLE
блок 2
count = 2
array_of_blocklengths = (3, 5 )
array_of_displacements = (0, addr1 - addr0 )
array_of_types = (MPI_INT, MPI_DOUBLE )
9
Структурный тип данн
блок 1
struct buff {
int ival[3];
double dval[5];
}
buf_datatype
Пустая область памяти, если double
хранится с 8-байтным смещением
блок 2
▪ Каждый массив хранится независимо.
▪ Каждый буфер – это пара массива из 3 переменных int и массива из 5 переменных
double
▪ Длина пустой области памяти между массивами может быть любая.
10
Как расчитать смещение
int MPI_Get_address(void* location, MPI_Aint* address);
▪ array_of_disp[i] = address(block_i) – address(block_0)
Как получить адрес в формате MPI:
11
Фиксация типа данных
int MPI_Type_commit(MPI_Datatype * datatype);
▪ Перед тем, как использовать новый тип данных, его необходимо активировать с
помощью функции MPI_Type_commit
▪ Это необходимо сделать только один раз
12
Размер и протяжённость типа данных
▪ Размер – количество байт, которые необходимо передать.
▪ Протяжённость – промежуток от первого до последнего типа
▪ Для базовыйх типов размер равен протяжённости и равен числу байт,
используемых компилятором.
▪ Для производных типов данных это не так:
старый тип
новый тип
▪ size = 6 * sizeof(oldtype)
▪ extent = 8 * sizeof(oldtype)
int MPI_Type_size(MPI_Datatype datatype, int * size )
int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint * extent )
int MPI_Type_get_extent(MPI_Datatype datatype,
MPI_Aint * lb , MPI_Aint * extent )
13
Пример
rank
0
sendbuf
2
recvbuf
2
sum
2
rank
1
sendbuf
0
recvbuf
0
sum
0
rank
2
sendbuf
recvbuf
1
sum
1
Процесс 0
Процесс 1
Процесс 2
1
1
1
2
2
2
3
3
3
4
4
4
5
5
5
1 1.0
2 2.0
0 0.0
0 0.00 0.0
1 1.0
2 2.0 2 2.0
1 1.01 1.0
2 2.0
0 0.0
Управление
коммуникаторами
15
Создание новых коммуникаторов
MPI_Comm_split(MPI_Comm comm, int color, int key,
MPI_Comm* newcomm)
▪ Функция создаёт новый коммуникатор путём разделения старого коммуникатора на
под-коммуникаторы на основе color и key.
▪ Старый коммуникатор остаётся.
▪ Каждый процесс передаёт своё значение color, который определяет, к какому
коммуникаторы он принадлежит.
▪ Если значение color равно MPI_UNDEFINED, то процесс не включается ни в один
новый коммуникатор.
▪ key определяет ранг процесса в новом коммуникаторе
16
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
MPI_COMM_WORLD
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Создание новых коммуникаторов
COMM1, COMM2, COMM3, COMM4
17
Создание новых коммуникаторов
int world_rank, world_size;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// Определить цвет (номер строки)
int color = world_rank / 4;
// Разбить коммуникатор
MPI_Comm row_comm;
MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm);
int row_rank, row_size;
MPI_Comm_rank(row_comm, &row_rank);
MPI_Comm_size(row_comm, &row_size);
printf("WORLD RANK/SIZE: %d/%d t ROW RANK/SIZE: %d/%dn",
world_rank, world_size, row_rank, row_size);
MPI_Comm_free(&row_comm);
18
Группы процессов
▪ Коммуникатор – это композиция контекста и группы процессов.
▪ Группа процессов – это множество процессов.
▪ Группы, подобно множествам, поддерживают операции объединения и
пересечения.
0 2 2 4
1 3 3 5
0 2 2 4
1 3 3 5
Объединение
Пересечени
е
2
3
0 2 4
1 3 5
19
Группы процессов
MPI_Comm_group(MPI_Comm comm, MPI_Group* group)
MPI_Group_union(MPI_Group group1, MPI_Group group2,
MPI_Group* newgroup)
MPI_Comm_create_group(MPI_Comm comm, MPI_Group group, int tag,
MPI_Comm* newcomm)
MPI_Group_intersection(MPI_Group group1, MPI_Group group2,
MPI_Group* newgroup)
Создание группы
Объединение групп:
Пересечение групп:
Создание нового коммуникаторы из группы:
20
Создание новых коммуникаторов
int world_rank, world_size;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Group world_group;
MPI_Comm_group(MPI_COMM_WORLD, &world_group);
int n = 7;
const int ranks[7] = {1, 2, 3, 5, 7, 11, 13};
// Создать группу из из world_group и включить в неё 7 процессов
MPI_Group prime_group;
MPI_Group_incl(world_group, 7, ranks, &prime_group);
// Создать новый коммуникатор
MPI_Comm prime_comm;
MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm);
int prime_rank = -1, prime_size = -1;
if (MPI_COMM_NULL != prime_comm) {
MPI_Comm_rank(prime_comm, &prime_rank);
MPI_Comm_size(prime_comm, &prime_size);
}
printf("WORLD RANK/SIZE: %d/%d t PRIME RANK/SIZE: %d/%dn",
world_rank, world_size, prime_rank, prime_size);
MPI_Group_free(&world_group);
MPI_Group_free(&prime_group);
MPI_Comm_free(&prime_comm);

Лекция 4. Производные типы данных в стандарте MPI

  • 1.
    Лекция 4. Производныетипы данных в MPI Пазников Алексей Александрович Параллельные вычислительные технологии СибГУТИ (Новосибирск) Осенний семестр 2015 www: cpct.sibsutis.ru/~apaznikov/teaching q/a: piazza.com/sibsutis.ru/fall2015/pct2015fall
  • 2.
  • 3.
    3 Производные типы данных(derived datatypes) Сообщения содержат некоторое число элементов некоторого типа данных. В MPI типы бывают ▪ Базовые (basic) ▪ Производные (derived), которые могут быт получены из базовых и производных типов. Примеры сообщений в MPI Соответствия между базовыми типами MPI и типами С:
  • 4.
    4 Производные типы данных(derived datatypes) struct buff_s { int ival[3]; double dval[5]; } buffer; Компилятор int double MPI_Send(&buffer, 1, buff_datatype, ...) &buffer = начальный адрес данных Указатель, описывающий смещение array_of_types[0]=MPI_INT; array_of_blocklengths[0]=3; array_of_displacements[0]=0; array_of_types[1]=MPI_DOUBLE; array_of_blocklengths[1]=5; array_of_displacements[1]=...; MPI_Type_struct(2, array_of_blocklengths, array_of_displacements, array_of_types, &buff_datatype ); MPI_Type_commit(&buff_datatype);
  • 5.
    5 Производные типы данных(derived datatypes) базовый тип 0 Производные типы данных представляют собой список указателей на базовые типы данных. базовый тип 1 ... базовый тип n – 1 смещение для типа 0 смещение для типа 1 ... смещение для типа n – 1 c 11 22 2.343546e35Пример: 0 4 8 12 16 20 24 MPI_CHAR MPI_INT MPI_INT MPI_DOUBLE 0 4 8 16 Производные типы данных описывают расположение объектов в памяти, например, для структур, блоков, массивов и т.д.
  • 6.
    6 Непрерывные данные ▪ Самыйпростой вид производных типов. ▪ Состоит из числа непрерывных элементов одного типа старый тип новый тип int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype);
  • 7.
    7 Векторный тип данных blocklength= 3 элемента в блоке int MPI_Type_vector(int count, int blocklength, int stride, MPI_Datatype oldtype, MPI_Datatype *newtype); старый тип новый тип stride = 5 (элементов между блоками) Пустые области памяти также необходимо передавать count = 2 (число блоков)
  • 8.
    8 Структурный тип данных блок1 int MPI_Type_vector(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types, MPI_Datatype *newtype); старый тип новый тип Пустая область памяти, если double хранится с 8-байтным смещением MPI_INT MPI_DOUBLE блок 2 count = 2 array_of_blocklengths = (3, 5 ) array_of_displacements = (0, addr1 - addr0 ) array_of_types = (MPI_INT, MPI_DOUBLE )
  • 9.
    9 Структурный тип данн блок1 struct buff { int ival[3]; double dval[5]; } buf_datatype Пустая область памяти, если double хранится с 8-байтным смещением блок 2 ▪ Каждый массив хранится независимо. ▪ Каждый буфер – это пара массива из 3 переменных int и массива из 5 переменных double ▪ Длина пустой области памяти между массивами может быть любая.
  • 10.
    10 Как расчитать смещение intMPI_Get_address(void* location, MPI_Aint* address); ▪ array_of_disp[i] = address(block_i) – address(block_0) Как получить адрес в формате MPI:
  • 11.
    11 Фиксация типа данных intMPI_Type_commit(MPI_Datatype * datatype); ▪ Перед тем, как использовать новый тип данных, его необходимо активировать с помощью функции MPI_Type_commit ▪ Это необходимо сделать только один раз
  • 12.
    12 Размер и протяжённостьтипа данных ▪ Размер – количество байт, которые необходимо передать. ▪ Протяжённость – промежуток от первого до последнего типа ▪ Для базовыйх типов размер равен протяжённости и равен числу байт, используемых компилятором. ▪ Для производных типов данных это не так: старый тип новый тип ▪ size = 6 * sizeof(oldtype) ▪ extent = 8 * sizeof(oldtype) int MPI_Type_size(MPI_Datatype datatype, int * size ) int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint * extent ) int MPI_Type_get_extent(MPI_Datatype datatype, MPI_Aint * lb , MPI_Aint * extent )
  • 13.
  • 14.
  • 15.
    15 Создание новых коммуникаторов MPI_Comm_split(MPI_Commcomm, int color, int key, MPI_Comm* newcomm) ▪ Функция создаёт новый коммуникатор путём разделения старого коммуникатора на под-коммуникаторы на основе color и key. ▪ Старый коммуникатор остаётся. ▪ Каждый процесс передаёт своё значение color, который определяет, к какому коммуникаторы он принадлежит. ▪ Если значение color равно MPI_UNDEFINED, то процесс не включается ни в один новый коммуникатор. ▪ key определяет ранг процесса в новом коммуникаторе
  • 16.
    16 0 1 23 4 5 6 7 8 9 10 11 12 13 14 15 MPI_COMM_WORLD 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Создание новых коммуникаторов COMM1, COMM2, COMM3, COMM4
  • 17.
    17 Создание новых коммуникаторов intworld_rank, world_size; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); MPI_Comm_size(MPI_COMM_WORLD, &world_size); // Определить цвет (номер строки) int color = world_rank / 4; // Разбить коммуникатор MPI_Comm row_comm; MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &row_comm); int row_rank, row_size; MPI_Comm_rank(row_comm, &row_rank); MPI_Comm_size(row_comm, &row_size); printf("WORLD RANK/SIZE: %d/%d t ROW RANK/SIZE: %d/%dn", world_rank, world_size, row_rank, row_size); MPI_Comm_free(&row_comm);
  • 18.
    18 Группы процессов ▪ Коммуникатор– это композиция контекста и группы процессов. ▪ Группа процессов – это множество процессов. ▪ Группы, подобно множествам, поддерживают операции объединения и пересечения. 0 2 2 4 1 3 3 5 0 2 2 4 1 3 3 5 Объединение Пересечени е 2 3 0 2 4 1 3 5
  • 19.
    19 Группы процессов MPI_Comm_group(MPI_Comm comm,MPI_Group* group) MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group* newgroup) MPI_Comm_create_group(MPI_Comm comm, MPI_Group group, int tag, MPI_Comm* newcomm) MPI_Group_intersection(MPI_Group group1, MPI_Group group2, MPI_Group* newgroup) Создание группы Объединение групп: Пересечение групп: Создание нового коммуникаторы из группы:
  • 20.
    20 Создание новых коммуникаторов intworld_rank, world_size; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Group world_group; MPI_Comm_group(MPI_COMM_WORLD, &world_group); int n = 7; const int ranks[7] = {1, 2, 3, 5, 7, 11, 13}; // Создать группу из из world_group и включить в неё 7 процессов MPI_Group prime_group; MPI_Group_incl(world_group, 7, ranks, &prime_group); // Создать новый коммуникатор MPI_Comm prime_comm; MPI_Comm_create_group(MPI_COMM_WORLD, prime_group, 0, &prime_comm); int prime_rank = -1, prime_size = -1; if (MPI_COMM_NULL != prime_comm) { MPI_Comm_rank(prime_comm, &prime_rank); MPI_Comm_size(prime_comm, &prime_size); } printf("WORLD RANK/SIZE: %d/%d t PRIME RANK/SIZE: %d/%dn", world_rank, world_size, prime_rank, prime_size); MPI_Group_free(&world_group); MPI_Group_free(&prime_group); MPI_Comm_free(&prime_comm);