Вычислительный центр им. А.А. Дородницына РАН    

Раздел виртуального курса
Параллельное программирование в интерфейсе MPI


MPI коллективный обмен сообщениями II


Содержание

1.     Введение

2.     Расширенные особенности

3.     Сравнение Scatter и scatterv

2.1 Scatter (размещение "один-каждому")

2.2 Scatterv (гибкое размещение "один-каждому")

4.     Синтаксис MPI_Scatterv

5.     Синтаксис MPI_Gatherv

6.     Когда эти вызовы могут пригодиться?

6.1 Пример проблемы

6.2 Пример решения

Литература


1. Введение

В этом модуле будет более подробно рассказано о том, как использовать коллективные коммуникации scatter/gather, если: необходимо использовать разные процессы для того, чтобы получить/послать сообщения разных размеров; порядок, в котором данные поступают/забираются к/от процессу/-а, не совпадает с порядком рангов; данные корневого процесса не являются непрерывными.

2. Расширенные особенности

Цель этого сжатого модуля состоит в том, чтобы познакомиться с некоторыми особенностями MPI, которые допускают большую гибкость при использовании коллективных коммуникаций gather (сборка) или scatter (размещение).

MPI_Scatterv, MPI_Gatherv, MPI_Allgatherv, MPI_Alltoallv

Что обозначает "v"?

varying (переменный, меняющийся) √ размер, относительное местоположение сообщений

Примеры для обсуждения: MPI_Gatherv и MPI_Scatterv

Можно было бы сказать, что этот модуль обязан своим появлением на свет букве v! Все функции MPI, о которых мы будем говорить, оканчиваются на v - например, MPI_Scatterv. Можно считать, что v обозначает что-то типа "меняющийся (varying)" или "переменная (variable)". Это связано с тем, что функции, оканчивающиеся на v, позволяют изменять как (1) размер, так и (2) местоположение сообщений (используемых для коммуникации) в памяти. В качестве примеров для объяснения основных идей будем использовать MPI_Scatterv и MPI_Gatherv, при этом эти идеи могут быть легко распространены и на MPI_Allgatherv и MPI_Alltoallv.

Преимущества использования v:

  больше гибкости при записи кода

  меньше потребности в копировании данных в промежуточные буфера

  более компактный конечный код

  поставляемая реализация может быть оптимальной

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


3. Сравнение Scatter и Scatterv

3.1 Scatter (размещение "один-каждому")

Цель операции размещения "один-каждому":

  отправить различные порции данных различным процессам

  не совпадает с пересылкой ⌠один-всем■ broadcast (когда одна и та же самая порция данных переходит ко всем процессам)

Вызову Scatter необходимы непрерывные данные и однородный размер сообщения.

Для начала, сравним scatter и scatterv. Напомним, что цель функции scatter (размещения) состоит в том, чтобы отослать различные порции данных к различным задачам - то есть, происходит разбиение данных по отдаленным процессам. Напомним также, что scatter не совпадает с broadcast, поскольку broadcast ту же самую порцию данных отправляют каждому процессу. Как можно заметить из рисунка, основная функция MPI_Scatter требует, чтобы данные отправителя были сохранены в непрерывных адресах памяти и что порции данных являются однородными по размеру. Таким образом, первые N элементов данных отправляют первому процессу в коммуникаторе, следующие N элементов отправляют второму процессу, и.т.д. Однако, для некоторых приложений это может оказаться чрезмерным ограничением. Допустим, некоторым процессам надо меньше, чем N элементов. Есть ли необходимость в том, чтобы увеличивать буфер отправки ненужными данными? Ответ - нет: надо использовать MPI_Scatterv.


3.2 Scatterv (гибкое размещение "один-каждому")

Дополнительные возможности в scatterv:

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

(однако, индивидуальные сообщения все же должны быть непрерывными)

  разрешены нерегулярные размеры сообщения

  данные могут быть распределены по процессам в любом порядке

 

MPI_Scatterv предоставляет дополнительные возможности, которые можно без труда перечислить, если сравнить этот рисунок с предыдущим. Прежде всего, различным процессорам допустимо отправлять различное число элементов. К тому же, первый элемент в каждой порции данных не должен быть позиционирован по некоторому постоянному промежутку от первого элемента предыдущего куска. Более того, нет необходимости сохранять порции данных в правильной последовательности! Это и есть гибкость вызовов gatherv/scatterv, о которой раньше упоминали. Однако, есть и ограничения: сами порции данных должны быть непрерывными и не должны пересекаться друг с другом. Отметим, в качестве вводного примечания, что, если действительно необходимо, то промежутки могут быть вставлены в порции данных с помощью создания производного типа данных MPI, который имеет некоторый встроенный шаг по индексу (при считывании элементов многомерного массива). Но такой способ зачастую приводит к потере эффективности, более того, он все еще не устраняет второго ограничения (порции данных не должны пересекаться).

4. Синтаксис MPI_Scatterv

int sendcounts[nproc], displs[nproc];

...

MPI_Scatterv

(sendbuf, sendcounts, displs, sendtype,

recvbuf, recvcount, recvtype,

root, comm);

Синтаксис вызова MPI_Scatterv довольно просто, даже в некоторой степени самоочевидный. Сравнивая с обычным вызовом MPI_Scatter видим, что есть два отличия: аргумент sendcounts стал массивом, также был добавлен новый аргумент displs.

sendcounts[i] √ количество элементов типа sendtype, которые следует отправить от процесса root процессу i. Таким образом, значение этого аргумента существенно только для процесса root.

 

displs[i] √ смещение от sendbuf к началу i-го сообщения, в единицах sendtype.  Этот аргумент также имеет значение только для процесса root.

И это в значительной степени все различия! Другие параметры в списке просто отражают обычную последовательность вызовов MPI_Scatter.


5. Синтаксис MPI_Gatherv

Теперь опишем вызовы Gather (сборка) и Gatherv (гибкая сборка), которые, по сути, представляют собой полные противоположности вызово Scatter (размещение) и Scatterv. Фактически, даже нет необходимости в новых диаграммах! Достаточно просто изменить направление стрелок на противоположное в предыдущих диаграммах. Поэтому, обратим внимание непосредственно на синтаксис запроса Gatherv.

int recvcounts[nproc], displs[nproc];

...

MPI_Gatherv

( sendbuf, sendcount, sendtype,

recvbuf, recvcounts, displs, recvtype,

root, comm, ierror )

В данном случае аргумент recvcounts[i] играет туже роль, что и аргумент sendcounts[i] в вызове MPI_Scatterv. Положение этого аргумента в списке аргументов было сдвинуто таким образом, чтобы он находился среди аргументов, относящихся к получателю. При этом displs[i] указывает на то, где необходимо поместить данные, прибывающие от процесса i. Эта информация задается как смещение относительно адреса recvbuf процесса root, причем смещение задано в единицах sendtype. Итак, при сравнении с MPI_Gather видно, что основные отличия в следующем: появился дополнительный массив displs; аргумент recvcounts стал массивом.


6. Когда эти вызовы могут пригодиться?

6.1 Пример проблемы

Пример: Процесс 0 считывает исходные данные и распределяет их по
другим процессам, в соответствии с декомпозицией области определения.


Проблема: Что, если точки не могут быть разделены равномерно?

Типичный ситуация, когда имеет смысл использовать MPI_Scatterv, возникает при распределении начальных данных в код с параллельными данными. Другими словами, происходит распараллеливание кода посредством декомпозиции области определения. Очевидно, что если число точек не делится на число процесса, то не все процессы получат одно и то же самое число точек данных. На этом упрощенном примере процесс 0 изначально имеет все данные, в то время как цвета указывают возможное целевое распределение точек данных по процессам включая процесс 0.


6.2 Пример решения

nmin=npts/nprocs;

nextra=npts%nprocs;

k=0;

for(i=0;i< nprocs-1;i++)

{

if(i< nextra) sendcounts[i]=nmin+1;

else sendcount[i]=nmin;

displa[i]=k;

k=k+sendcounts[i];

}

MPI_Scatterv(sendbuf,sendcounts,displa ...);

В приведенной программе множество точек распределяется по процессам настолько равномерно, насколько это возможно. Вначале, с помощью использования функции modulo, выясняется, сколько есть дополнительных точек. Затем запускается цикл по процессам. Через массив sendcounts добавляется по одной дополнительной точке каждому процессу, пока все дополнительные точки nextra не будут учтены. И, пока происходит это добавление, также отслеживаются относительные стартовые местоположения порций данных с помощью массива displs. Наконец, вызывается MPI_Scatterv с расчетными массивами sendcounts и displs, чтобы выполнить желаемое распределение данных.


Литература

Домашняя страница MPI в Argonne National Labs http://www.mcs.anl.gov/mpi

[Example]Выборка примеров программ, которые доступны в Интернете и иллюстрируют команды, охваченные в этом модуле.


 

 

╘ 2003 Вычислительный центр им. А.А.Дородницына Все права защищены.
Прочтите наше Copyright руководство.