Производные типы и упаковка/распаковка данных
Производные типы данных. Основные понятия
Производные типы данных. Основные понятия
Производные типы данных. Основные понятия
Стандартный сценарий определения и использования производных типов
Конструкторы производных типов Простейший конструктор
Векторный конструктор
Векторно-байтовый конструктор
Конструктор произвольных блоков с произвольными смещениями
Конструктор произвольных блоков с произвольными смещениями, пример
Конструктор произвольных блоков с произвольными байтовыми смещениями
Конструктор одинаковых блоков с произвольными смещениями
Самый универсальный конструктор
Пример применения конструктора MPI_Type_create_struct
Модификация нижней и верхней границ
Регистрация производных типов, создание дубликатов и уничтожение
Характеристики типов данных
Получение протяженности и размера типа данных
Уничтожение типа данных
Пример использования производных типов данных
Пример использования производных типов данных
Пример использования производных типов данных
Пример использования производных типов данных
Результаты примера:
Передача/прием упакованных данных.
Передача/прием упакованных данных.
Передача/прием упакованных данных.
Пример использования функций упаковки/распаковки
Пример использования функций упаковки/распаковки
Пример использования функций упаковки/распаковки
Пример использования функций упаковки/распаковки
Результаты:
Группы и коммуникаторы
Объекты и механизмы
Коммуникаторы
Коммуникаторы
Управление группами.
Операции с группами ветвей
Операции с группами ветвей
Сравнение групп ветвей
Создание новой группы ветвей
Создание новой группы ветвей
Создание новой группы ветвей
Пример использования:
830.50K
Категория: ПрограммированиеПрограммирование

parProgr10

1. Производные типы и упаковка/распаковка данных

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

2. Производные типы данных. Основные понятия

Производные типы MPI не являются в полном
смысле типами данных, как это понимается в
языках программирования. Они не могут
использоваться ни в каких других операциях, кроме
коммуникационных. Производные типы MPI
следует понимать просто как описатели
расположения в памяти элементов базовых типов.
Производный тип MPI представляет собой скрытый
(opaque) объект, который специфицирует две вещи:
◦ последовательность базовых типов и
◦ последовательность их смещений.
Упорядоченный набор таких пар называется
отображением (картой) типа:
Typemap = { ( type0, disp0 ), ( type1, disp1 ), ...,
( typen-1, dispn-1 ) }

3. Производные типы данных. Основные понятия

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

4. Производные типы данных. Основные понятия

При параллельном программировании для систем с
распределенной памятью типичной является
ситуация, когда каждая ветвь программы в
определенные моменты времени должна передавать
другим ветвям совокупности данных, находящихся в
ее памяти не компактно, а рассредоточенно,
например:

ti
ветвь k+1
ti+1
ветвь k+2
ti+2
ветвь k+3

ti+n
ветвь k+n+1

5. Стандартный сценарий определения и использования производных типов

Производный тип строится из предопределенных
типов MPI и, возможно, ранее определенных
производных типов с помощью специальных функцийконструкторов.
Новый производный тип регистрируется вызовом
функции MPI_Type_commit.
После регистрации новый производный тип можно
использовать в коммуникационных операциях и при
конструировании других типов. Предопределенные
типы MPI считаются зарегистрированными.
С производными типами можно выполнять
некоторые дополнительные операции (в частности –
узнавать их размер, протяженность и некоторые
другие характеристики).
Когда производный тип становится ненужным, он
уничтожается функцией MPI_Type_free.

6. Конструкторы производных типов Простейший конструктор

Самый простой конструктор типа MPI_Type_contiguous
создает новый тип, элементы которого состоят из
указанного числа элементов базового типа,
занимающих смежные области памяти:
int MPI_Type_contiguous ( int count, MPI_Datatype
oldType, MPI_Datatype *newType );
Пример:
MPI_Datatype newType;

MPI_Type_contiguous( 5, MPI_ REAL, &newType );
oldType:
- 4-х байтное вещественное число
newType:
- 20 байт

7. Векторный конструктор

Конструктор типа MPI_Type_vector создает тип,
каждый элемент которого представляет собой
несколько равноудаленных друг от друга блоков из
одинакового числа смежных элементов базового типа.
int MPI_Type_vector( int count, int blockLength, int
stride, MPI_Datatype oldType, MPI_Datatype *newType);
Функция создает тип newType, элемент которого
состоит из count блоков, каждый из которых содержит
одинаковое число blockLength элементов типа oldType.
Шаг stride между началом блока и началом
следующего блока всюду одинаков и кратен
протяженности представления базового типа.
Пусть, например, была вызвана эта функция с
такими аргументами:
MPI_Type_vector (4, 1, 4, MPI_ REAL, &newType);
newType:

8. Векторно-байтовый конструктор

Конструктор типа MPI_Create_hvector (есть
устаревший 32-х битный вариант - MPI_Type_hvector)
расширяет возможности конструктора MPI_
Create_vector, позволяя задавать произвольный шаг
между началами блоков в байтах.
int MPI_ Create_hvector( int count, int blockLength,
MPI_Aint stride, MPI_Datatype oldType,
MPI_Datatype *newType)
Пусть, например, была вызвана эта функция с такими
аргументами:
MPI_Create_hvector(3,2,18,MPI_INTEGER,&newType);
newType:

9. Конструктор произвольных блоков с произвольными смещениями

Конструктор типа MPI_Type_indexed является
более универсальным конструктором по сравнению
с MPI_Type_vector, так как элементы создаваемого
типа состоят из произвольных по длине блоков с
произвольным смещением блоков от начала
размещения элемента. Смещения задаются в
элементах старого типа.
int MPI_Type_indexed( int count , int
*array_of_blockLengths, int *array_of_displacements,
MPI_Datatype oldType, MPI_Datatype *newType );
Эта функция создает тип newType, каждый
элемент которого состоит из count блоков, где i-ый
блок содержит array_of_blockLengths[i] элементов
базового типа и смещен от начала размещения
элемента нового типа на array_of_displacements[i]
элементов базового типа.

10. Конструктор произвольных блоков с произвольными смещениями, пример

Пусть, например, этот конструктор был вызван с
такими аргументами:
MPI_Type newType;
int blk[ ] = { 2, 3, 1 };
int dsp[ ] = { 0, 6, 3 };
MPI_Type_indexed (3, blk, dsp, MPI_INTEGER,
&newType );
Будет образован тип данных, первый элемент
которого содержит 2 целых и размещается по
нулевому смещению от условного начала, второй
элемент содержит 3 целых и расположен по
смещению 6 (в целых числах) и третий элемент – 1
целое по смещению 3:

11. Конструктор произвольных блоков с произвольными байтовыми смещениями

Конструктор типа MPI_Type _Create_hindexed
(устаревшая 32-битная версия называется
MPI_Type_hindexed) идентичен конструктору
MPI_Type_indexed за исключением того, что смещения
измеряются в байтах.
int MPI _Type_Create_hindexed( int count , int
*array_of_blockLengths, MPI_Aint
*array_of_displacements, MPI_Datatype oldType,
MPI_Datatype *newType );
Элемент нового типа состоит из count блоков, где iый блок содержит array_of_blockLengths[ i ] элементов
базового типа и смещен от начала размещения
элемента нового типа на array_of_displacements[ i ]
байт.

12. Конструктор одинаковых блоков с произвольными смещениями

Конструктор типа
MPI_Type_create_indexed_block идентичен
конструктору MPI_Type_indexed за исключением
того, что все блоки одинаковы.
int MPI_Type_create_indexed_block( int count,
int blockLength, MPI_Aint array_of_displacements,
MPI_Datatype oldType, MPI_Datatype *newType );
Элемент нового типа состоит из count блоков,
где i-ый блок содержит blockLength элементов
базового типа и смещен от начала размещения
элемента нового типа на
array_of_displacements[i] байт.

13. Самый универсальный конструктор

Конструктор типа MPI_Type_create_struct (устаревшая
32-битная версия называется MPI_Type_struct ) –
самый универсальный из всех конструкторов типа.
Создаваемый им тип является структурой, состоящей
из произвольного числа блоков, каждый из которых
может содержать произвольное число элементов
одного из базовых типов и может быть смещен на
произвольное число байтов от начала размещения.
int MPI_Type_create_struct( int count ,
int *array_of_blocklengths, MPI_Aint
*array_of_displacements, MPI_Datatype *array_of_types,
MPI_Datatype *newType);
Функция создает тип newType, элемент которого
состоит из count блоков, где i-ый блок содержит
array_of_blocklengths[i] элементов типа array_of_types[i].
Смещение i-ого блока от начала размещения элемента
нового типа измеряется в байтах и задается в
array_of_displacements[i].

14. Пример применения конструктора MPI_Type_create_struct

Пусть, например, была вызвана эта функция с такими
аргументами:
MPI_Datatype newType;
int blk[ ] = { 2, 1, 2 };
int dsp[ ] = { 0, 4, 0 };
MPI_Datatype basics = { MPI_SHORT, MPI_REAL,
MPI_INTEGER };
MPI_Type_create_struct (3, blk, dsp, basics, &newType);
MPI_Short
MPI_Real
MPI_Integer

15. Модификация нижней и верхней границ

Часто бывает удобно явно указывать нижнюю и
верхнюю границы карты типа. Это позволяет
определить тип данных, который имеет «дыры» в
начале или конце, или тип с элементами, которые
находятся выше верхней или ниже нижней границы.
Программист также может пожелать обойти правила
выравнивания для некоторых структур данных внутри
своей программы или явно описать границы типа
данных, которые соответствуют этим структурам.
Для этого существует специальный конструктор:
int MPI_Type_create_resized( MPI_Datatype oldType,
MPI_Aint lb, MPI_Aint extent, MPI_Datatype *newType );
Эта функция возвращает в newType дескриптор
нового типа данных, идентичного oldType, за
исключением того, что нижняя граница типа данных
установлена в lb, а верхняя – в lb + extent. Любые
предущие маркеры lb и ub стираются, и в позиции,
указанные аргументами lb и extent помещается новая
пара маркеров. Это влияет на поведение типа данных
при коммуникациях с count>1, и при создании новых
порожденных типов данных.

16. Регистрация производных типов, создание дубликатов и уничтожение

Функция MPI_Type_commit регистрирует созданный
производный тип. Только после регистрации
новый тип может использоваться в
коммуникационных операциях.
int MPI_Type_commit( MPI_Datatype *dataType );
Любой зарегистрированный тип данных можно
продублировать с помощью функции:
int MPI_Type_dup( MPI_Datatype dataType,
MPI_Datatype *newType );
Эта функция просто копирует handle
существующего типа данных в указанную
переменную.

17. Характеристики типов данных

Любой тип данных в MPI имеет две характеристики:
протяженность и размер, выраженные в байтах:
Протяженность типа определяет, сколько байт
переменная данного типа занимает в памяти. Эта
величина может быть вычислена как
адрес последней ячейки данных – адрес первой
ячейки данных + длина последней ячейки данных
Протяженность может быть получена с помощью
функции MPI_Type_get_extent (устаревшая версия MPI_Type_extent), а если к типу применялись операции
изменения нижней и/или верхней границы, то нужно
использовать функцию MPI_Type_get_true_extent.
Размер типа определяет количество реально
передаваемых байт в коммуникационных операциях.
Эта величина равна сумме длин всех базовых
элементов определяемого типа. Размер типа данных
MPI может быть получен с помощью функции
MPI_Type_size.
Для простых типов протяженность и размер
совпадают.

18. Получение протяженности и размера типа данных

Функция MPI_Type_get_extent определяет
протяженность элемента указанного типа:
int MPI_Type_get_extent( MPI_Datatype dataType,
MPI_Aint *lb, MPI_Aint *extent );
extent – возвращаемая протяженность элемента
указанного типа.
lb – нижняя граница элемента заданного типа.
Лучше пользоваться функцией
MPI_Type_get_true_extent, которая возвращает истинные
нижнюю границу и протяженность даже в том случае, если
для данного типа изменялась нижняя граница:
int MPI_Type_get_true_extent( MPI_Datatype dataType,
MPI_Aint *lb, MPI_Aint *extent );
Функция MPI_Type_size определяет “чистый” размер
элемента некоторого типа (за вычетом пустых
промежутков), т.е. количество байт данных, которые будут
фактически передаваться при коммуникациях между
ветвями:
int MPI_Type_size( MPI_Datatype dataType, int *size );

19. Уничтожение типа данных

Функция MPI_Type_free уничтожает описатель
производного типа.
int MPI_Type_free( MPI_Datatype *dataType );
Функция MPI_Type_free устанавливает описатель
(handle) типа в состояние MPI_DATATYPE_NULL.
Это не повлияет на выполняющиеся в данный
момент коммуникационные операции с этим
типом данных и на производные типы, которые
ранее были определены через уничтоженный
тип.

20. Пример использования производных типов данных

#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#define TEST 10
int main( int argc, char *argv[ ] ) {
int Rank;
int Size;
MPI_Datatype newType;
MPI_Status status;
MPI_Init( &argc, &argv );
MPI_Comm_rank ( MPI_COMM_WORLD, &Rank );
MPI_Comm_size ( MPI_COMM_WORLD, &Size );
int sb[ 10 ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int rb[ 10 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1

21. Пример использования производных типов данных

2
MPI_Type_vector( 3, 2, 4, MPI_INT, &newType );
MPI_Type_commit( &newType );
int size, elements;
if( Rank == 0 ){
MPI_Type_size( newType, &size );
printf( "%d\n", size );
MPI_Send( sb, 1, newType, 1, TEST,
MPI_COMM_WORLD );
MPI_Send( sb, 1, newType, 1, TEST,
MPI_COMM_WORLD );
} else {
MPI_Recv( rb, 1, newType, 0, TEST,
MPI_COMM_WORLD, &status );
MPI_Get_elements( &status, newType, &elements
);
MPI_Get_count( &status, newType, &size );

22. Пример использования производных типов данных

3
printf( "Rank = %d, elements = %d, count = %d, rb =
%d,%d, %d, %d, %d, %d, %d, %d, %d, %d\n",
elements, size, Rank, rb[0], rb[1], rb[2], rb[3],
rb[4], rb[5], rb[6], rb[7], rb[8], rb[9] );
for( int i = 0; i<10; i++)
rb[ i ] = 0;
MPI_Recv( rb, 6, MPI_INT, 0, TEST,
MPI_COMM_WORLD, &status );
MPI_Get_elements( &status, MPI_INT, &elements );
MPI_Get_count( &status, MPI_INT, &size );

23. Пример использования производных типов данных

4
printf( "Rank = %d, elements = %d, count = %d,
rb = %d, %d, %d, %d, %d, %d, %d, %d, %d,
%d\n", elements, size, Rank, rb[0],rb[1],rb[2],
rb[3], rb[4], rb[5], rb[6], rb[7], rb[8], rb[9] );
}
MPI_Finalize( );
return 0;
}

24. Результаты примера:

24
Rank = 1, elements = 6, count = 1, rb = 0, 1, 0, 0, 4, 5, 0,
0, 8, 9
Rank = 1, elements = 6, count = 6, rb = 0, 1, 4, 5, 8, 9, 0,
0, 0, 0

25. Передача/прием упакованных данных.

1
Функция MPI_Pack упаковывает элементы
предопределенного или производного типа, помещая
их побайтное представление в выходной буфер:
int MPI_Pack( void* inBuf, int inCount, MPI_Datatype
dataType, void *outBuf, int outSize, int *position,
MPI_Comm comm);
Функция MPI_Pack упаковывает inCount элементов
типа dataType из области памяти с начальным
адресом inBuf. Результат упаковки помещается в
выходной буфер с начальным адресом outBuf и
размером outSize байт. Параметр position указывает
текущую позицию в байтах, начиная с которой будут
размещаться упакованные данные. После возврата из
функции значение position увеличивается на число
упакованных байт, указывая на первый свободный
байт. Параметр comm при последующей посылке
упакованного сообщения будет использован как
коммуникатор.

26. Передача/прием упакованных данных.

2
Функция MPI_Pack_size позволяет предварительно
(перед вызовом функции MPI_Pack) определить
размер буфера, необходимый для упаковки
заданного количества данных типа dataType.
int MPI_Pack_size( int inCount, MPI_Datatype
dataType, MPI_Comm comm, int *size );
После обращения к функции переменная size будет
содержать размер в байтах, необходимый для
упаковки inCount элементов типа dataType.

27. Передача/прием упакованных данных.

Функция MPI_Unpack извлекает заданное число
элементов некоторого типа из побайтного
представления элементов во входном массиве.
int MPI_Unpack( void* inBuf, int inSize, int *position,
void *outBuf, int outCount, MPI_Datatype dataType,
MPI_Comm comm );
Функция MPI_Unpack извлекает outCount
элементов типа dataType из побайтного
представления элементов в массиве inBuf,
начиная с адреса position. После возврата из
функции параметр position увеличивается на
размер распакованного сообщения. Результат
распаковки помещается в область памяти с
начальным адресом outBuf.
3

28. Пример использования функций упаковки/распаковки

1
#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#define TAG 10
int main( int argc, char *argv[ ] ) {
int rank;
int commSize;
MPI_Datatype newType;
MPI_Status status;
MPI_Init( 0, 0 );
MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
MPI_Comm_size ( MPI_COMM_WORLD, &commSize );
int sb[10] = { -1, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int rb[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int size, elements, position = 0;

29. Пример использования функций упаковки/распаковки

2
MPI_Type_vector( 3, 2, 4, MPI_INT, &newType );
MPI_Type_commit( &newType );
if( rank == 0 ){
MPI_Send( sb, 1, newType, 1, TAG,
MPI_COMM_WORLD );
MPI_Pack_size( 1, newType,
MPI_COMM_WORLD, &size );
MPI_Pack( sb, 1, newType, rb, size, &position,
MPI_COMM_WORLD );
printf( "rank = %d, size = %d, rb = %d, %d, %d,
%d, %d, %d, %d, %d, %d, %d, %d, %d\n",
rank, size, rb[0], rb[1], rb[2], rb[3], rb[4],
rb[5], rb[6], rb[7], rb[8], rb[9] );
MPI_Send( rb, size, MPI_PACKED, 1, TAG,
MPI_COMM_WORLD );
}

30. Пример использования функций упаковки/распаковки

3
else {
MPI_Recv( rb, 1, newType, 0, TAG,
MPI_COMM_WORLD, &status );
MPI_Get_elements( &status, newType, &elements );
MPI_Get_count( &status, newType, &size );
printf( "rank = %d, elements = %d, count = %d, rb =
%d, %d, %d, %d, %d, %d, %d, %d, %d,
%d\n", rank, elements, size, rb[0], rb[1], rb[2],
rb[3], rb[4], rb[5], rb[6], rb[7], rb[8], rb[9] );
for( int i = 0; i<10; rb[ i++ ]=0 );
MPI_Recv( rb, 10, MPI_INT, 0, TAG,
MPI_COMM_WORLD, &status );
MPI_Get_elements( &status, MPI_INT, &elements );
MPI_Get_count( &status, MPI_INT, &size );

31. Пример использования функций упаковки/распаковки

4
printf( "rank = %d, elements = %d, count = %d, rb =
%d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
rank, elements, size, rb[0], rb[1], rb[2], rb[3],
rb[4], rb[5], rb[6], rb[7], rb[8], rb[9] );
for( int i = 0; i < 10; sb[ i++ ] = -123 );
MPI_Unpack( rb, 24, &position, sb, 1, newType,
MPI_COMM_WORLD );
printf( "rank = %d, elements = %d, count = %d, sb =
%d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
rank, elements, size, sb[0], sb[1], sb[2], sb[3],
sb[4], sb[5], sb[6], sb[7], sb[8], sb[9] );
}
MPI_Finalize( );
return 0;
}

32. Результаты:

rank = 0, size = 24, rb = -1, 1, 4, 5, 8, 9, 0, 0, 0, 0
rank = 1, elements = 6, count = 1, rb = -1, 1, 0, 0, 4, 5, 0,
0, 8, 9
rank = 1, elements = 6, count = 6, rb = -1, 1, 4, 5, 8, 9, 0,
0, 0, 0
rank = 1, elements = 6, count = 6, sb = -1, 1, -123, -123,
4, 5,
-123, -123, 8, 9

33. Группы и коммуникаторы

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

34. Объекты и механизмы

Для достижения этих целей в интерфейсе
предусмотрены следующие объекты и
механизмы
Группы ветвей (groups)
Коммуникаторы (communicators)
Виртуальные топологии (virtual topologies)
Контексты связи (contexts)
Кэширование атрибутов (attribute caching)

35. Коммуникаторы

Коммуникаторы определяют области действия
1
любых операций обмена данными в MPI.
Коммуникаторы разделяются на два вида: интракоммуникаторы (внутригрупповые коммуникаторы),
предназначенные для операций в пределах
отдельной группы процессов, и интер-коммуникаторы
(межгрупповые коммуникаторы), предназначенные
для обменов между двумя группами процессов
(появились в стандарте MPI-2, для обеспечения
взаимодействия с вновь порождаемыми группами
процессов).
До сих пор использовался только предопределенный
коммуникатор MPI_COMM_WORLD, определяющий
единый контекст и совокупность всех
выполняющихся ветвей параллельной программы.

36. Коммуникаторы

В MPI имеются средства создания и изменения
2
коммуникаторов, предоставляющие возможность
программисту создавать собственные группы ветвей,
что делает возможным использование в программах
достаточно сложных схем взаимодействия. Типичной
проблемой, которую может решить использование
коммуникаторов, является проблема недопущения
"пересечения" обменов по тегам. В самом деле, если
программист использует какую-то библиотеку
параллельных методов, то он часто не знает, какие
теги использует эта библиотека при передаче
сообщений. В таком случае существует опасность, что
тег, выбранный программистом, совпадет с одним из
тегов, используемых библиотекой.
Чтобы этого не произошло, программист может
создать собственный коммуникатор и выполнять
операции обмена в его рамках. Несмотря на то, что
этот коммуникатор будет содержать те же ветви
программы, и такие же теги, что и коммуникатор
библиотеки, обмены в рамках этих двух разных
коммуникаторов "не пересекутся".

37. Управление группами.

С понятием коммуникатора тесно связано понятие
группы ветвей. Под группой понимают упорядоченное
множество ветвей программы. Каждой ветви в группе
соответствует уникальный номер - ранг. Группа отдельное понятие MPI, и операции с группами могут
выполняться отдельно от операций с коммуникаторами,
но операции обмена для указания области действия
всегда используют коммуникаторы, а не группы. Таким
образом, одна ветвь или группа ветвей могут входить в
несколько различных коммуникаторов. С группами
ветвей в MPI допустимы следующие действия:
Объединение групп;
Пересечение групп;
Образование разности групп.

38. Операции с группами ветвей

Новая группа может быть создана только из уже
существующих групп. В качестве исходной группы при
создании новой может быть использована группа,
связанная с предопределенным коммуникатором
MPI_COMM_WORLD. При ручном конструировании
групп может оказаться полезной специальная пустая
группа MPI_COMM_EMPTY. Однажды созданная группа
не может быть изменена. Она может быть только
уничтожена (освобождена).
Для получения дескриптора группы ветвей, связанной
с коммуникатором comm, используется функция:
int MPI_Comm_group( MPI_Comm comm, MPI_Group
*group );
Размер группы (количество ветвей в ней) можно
получить с помощью функции:
int MPI_Group_size(MPI_Group group, int *size)
Номер ветви в группе можно определить, вызвав
функцию:
int MPI_Group_rank( MPI_Group group, int *rank);

39. Операции с группами ветвей

Если есть две группы ветвей (неважно, каким образом
они были образованы), то с помощью функции:
int MPI_Group_translate_ranks( MPI_Group group1, int n,
int *ranks1, MPI_Group group2, int *ranks2 );
можно установить соответствие между номерами
ветвей в этих группах.
Аргументы:
group1 – первая группа ветвей;
n – число ветвей, для которых устанавливается
соответствие;
ranks1 – массив номеров ветвей из 1-й группы;
group2 – вторая группа;
ranks2 – массив, в который будут записаны номера
тех же ветвей во второй группе. Если ветвь из
первой группы отсутствует во второй группе, то для
нее будет записано значение MPI_UNDEFINED.

40. Сравнение групп ветвей

Две группы ветвей можно сравнить:
int MPI_Group_compare( MPI_Group group1,
MPI_Group group2, int *result );
Аргументы:
group1 – первая группа ветвей;
group2 – вторая группа;
result – переменная, получающая значение в
результате сравнения:
MPI_IDENT, если все элементы двух групп
одинаковы и следуют друг за другом в
одинаковом порядке;
MPI_SIMILAR, если все элементы двух групп
одинаковы, но порядок следования различен;
MPI_UNEQUAL – во всех остальных случаях.

41. Создание новой группы ветвей

Создание новой группы из явно указанных ветвей
старой группы:
int MPI_Group_incl( MPI_Group oldGroup, int n,
int *ranks, MPI_Group *newGroup );
Аргументы:
oldGroup – существующая группа ветвей;
n – размер вновь создаваемой группы (и массива
ranks);
ranks – массив номеров ветвей старой группы,
которые должны войти в новую группу;
newGroup – указатель (handle) создаваемой группы.
Функция MPI_Group_incl создает новую группу
newGroup, которая состоит из ветвей существующей
группы, перечисленных в массиве ranks. Ветвью с
номером i в новой группе является ветвь с номером
ranks[i] в существующей группе. Каждый элемент в
массиве ranks должен иметь корректный номер ветви
из группы oldGroup, и среди этих элементов не
должно быть совпадающих.

42. Создание новой группы ветвей

Создание новой группы из таких ветвей старой
группы, которые не принадлежат явно указанному
перечню:
int MPI_Group_excl( MPI_Group oldGroup, int n, int
*ranks, MPI_Group *newGroup );
Аргументы:
oldGroup – существующая группа ветвей;
n – размер создаваемой группы (массива ranks);
ranks – массив номеров ветвей старой группы,
которые НЕ должны войти в новую группу;
newGroup – указатель (handle) создаваемой группы.
Функция MPI_Group_excl создает новую группу
newGroup, которая состоит из всех ветвей
существующей группы, за исключением ветвей,
перечисленных в массиве ranks. Каждый элемент в
массиве ranks должен иметь корректный номер ветви
из группы oldGroup, и среди этих элементов не должно
быть совпадающих, иначе вся программа завершится
аварийно.

43. Создание новой группы ветвей

Есть две функции, являющиеся обобщением
предыдущих функций :
int MPI_Group_range_incl( MPI_Group oldGroup,
int n, int ranges[ ][ 3 ], MPI_Group *newGroup );
int MPI_Group_range_excl( MPI_Group oldGroup,
int n, int ranges[ ][ 3 ], MPI_Group *newGroup );
Аргументы:
oldGroup – существующая группа ветвей;
n – размер массива ranges;
ranges – массив триплетов (следующий слайд);
newGroup – указатель создаваемой группы.
Вместо одномерных массивов номеров ranks двумерные массивы триплетов, каждый из которых
имеет вид:
<нижняя граница> <верхняя граница> <шаг>
Границы и шаг должны определять допустимые
номера ветвей, причем все номера ветвей новой
группы должны быть различными

44. Пример использования:

Пример массива ranges:
int rngs[ ][ ] = {{ 2, 32768, 16 },{ 0, 131072, 256 }};
Здесь указано два триплета:
Триплет 2, 32768, 16 порождает последовательность
номеров 2, 18, 34, 50, 66, … 32738, 32754 (всего
2047 номеров)
Триплет 0, 131072, 256 порождает
последовательность номеров 0, 256, 512, …
130816, 131072 (всего 512 номеров)
Эти функции удобно применять в программах для
суперкомпьютеров с очень большим (десятки и
сотни тысяч) количеством ядер.
English     Русский Правила