Programming Taskbook


E-mail:

Пароль:

Регистрация пользователя   Восстановление пароля

 

ЮФУ SMBU

Электронный задачник по программированию

©  М. Э. Абрамян (Южный федеральный университет, Университет МГУ-ППИ в Шэньчжэне), 1998–2024

 

PT for MPI-2 | Решения | Операции редукции

PrevNext


Операции редукции и составные типы данных: MPI3Coll23

При выполнении очередного задания мы познакомимся с коллективными операциями редукции и с примером использования в MPI-программах составных типов данных (структур).

MPI3Coll23. В каждом процессе дан набор из K + 5 чисел, где K — количество процессов. Используя функцию MPI_Allreduce для операции MPI_MINLOC, найти минимальное значение среди элементов данных наборов с одним и тем же порядковым номером и ранг процесса, содержащего минимальное значение. Вывести в главном процессе минимумы, а в остальных процессах — ранги процессов, содержащих эти минимумы.

Большая группа функций MPI предназначена для организации коллективного взаимодействия процессов. «Коллективные» MPI-функции, в отличие от функций MPI_Send, MPI_Ssend и MPI_Recv (рассмотренных в предыдущем разделе), позволяют организовать обмен сообщениями не между двумя отдельными процессами (отправителем и получателем), а между всеми процессами, входящими в некоторый коммуникатор. В частности, при использовании коммуникатора MPI_COMM_WORLD можно организовать коллективный обмен сообщениями между всеми запущенными процессами параллельной программы.

Среди коллективных MPI-функций выделяют группу функций, обеспечивающих выполнение операций редукции, т. е. операций, связанных с пересылкой не исходных данных, а результатов их обработки некоторой групповой операцией: нахождением суммы MPI_SUM, произведения MPI_PROD, максимального MPI_MAX или минимального MPI_MIN значения и т. д. — всего в стандарте MPI предусмотрено 12 операций редукции, кроме того, программист может определять и свои собственные операции. Среди операций редукции особое место занимают операции MPI_MAXLOC и MPI_MINLOC, позволяющие найти не только максимальный или минимальный элемент среди элементов, предоставленных каждым процессом, но и его номер (в качестве номера обычно используется ранг процесса, предоставившего этот экстремальный элемент).

При запуске программы-заготовки, созданной для выполнения задания MPI3Coll23, мы увидим на экране окно задачника, подобное приведенному на следующем рисунке:

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

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

struct MINLOC_Data
{
  double a;
  int n;
};

Для хранения исходных данных в каждом процессе должен быть выделен массив элементов типа MINLOC_Data, и такой же массив должен использоваться для хранения результатов выполнения операции редукции. Размер набора данных, который придется хранить в этих массивах, заранее неизвестен, так как он связан с количеством процессов параллельной программы. Поэтому можно либо выделять память для массивов динамически (после того как программе станет известно число процессов size), либо использовать статические массивы, размер которых окажется достаточным для любых наборов исходных данных. При выполнении задания MPI3Coll23 мы будем использовать статические массивы (особенности, связанные с использованием динамических массивов, будут рассмотрены в следующем разделе). Запустив созданную программу- заготовку несколько раз, мы можем убедиться в том, что для данного задания количество процессов может меняться в диапазоне от 3 до 5. Таким образом, учитывая, что размер наборов исходных данных равен K + 5, где K — количество процессов, нам достаточно описать в функции Solve массивы размера 10:

MINLOC_Data d[10], res[10];

Инициализация исходного массива d должна выполняться в каждом процессе параллельной программы:

for (int i = 0; i < size + 5; ++i)
{
  pt >> d[i].a;
  d[i].n = rank;
}

Запустив этот вариант программы, мы получим сообщение о том, что все исходные данные успешно введены:

Перед выводом результатов необходимо выполнить соответствующую коллективную операцию редукции. Она должна быть выполнена во всех процессах, после чего в главном процессе (ранга 0) надо вывести поле a каждого элемента результирующего массива res (т. е. минимальное значение, выбранное из всех элементов исходных массивов с данным индексом), а в остальных (подчиненных) процессах — поле n (т. е. ранг процесса с этим минимальным значением):

MPI_Allreduce(d, res, size + 5, MPI_DOUBLE_INT, MPI_MINLOC,
  MPI_COMM_WORLD);
for (int i = 0; i < size + 5; ++i)
  if (rank == 0)
    pt << res[i].a;
  else
    pt << res[i].n;

Обратите внимание на два важных момента. Во-первых, массивы исходных и результирующих данных передаются в MPI-функции как указатели на их начальный элемент, поэтому в качестве первых двух параметров функции MPI_Allreduce просто указаны идентификаторы массивов d и res. Во-вторых, имя типа, указываемое в качестве четвертого параметра, должно соответствовать типу элементов обрабатываемых массивов (в данном случае надо указать один из стандартных типов MPI: MPI_DOUBLE_INT, соответствующий структуре из двух полей — вещественного и целого). В ситуациях, когда стандартных типов данных, предоставляемых библиотекой MPI, оказывается недостаточно, приходится определять новые типы MPI (этой теме посвящена группа заданий MPI4Type).

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

В заключение приведем полный текст решения задачи MPI3Coll23:

struct MINLOC_Data
{
  double a;
  int n;
};

void Solve()
{
  Task("MPI3Coll23");
  int flag;
  MPI_Initialized(&flag);
  if (flag == 0)
    return;
  int rank, size;
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MINLOC_Data d[10], res[10];
  for (int i = 0; i < size + 5; ++i)
  {
    pt >> d[i].a;
    d[i].n = rank;
  }
  MPI_Allreduce(d, res, size + 5, MPI_DOUBLE_INT, MPI_MINLOC,
    MPI_COMM_WORLD);
  for (int i = 0; i < size + 5; ++i)
    if (rank == 0)
      pt << res[i].a;
    else
      pt << res[i].n;
}

PrevNext

 

Рейтинг@Mail.ru

Разработка сайта:
М. Э. Абрамян, В. Н. Брагилевский

Последнее обновление:
01.01.2024