Введение в коллективное общение

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

Например, нам нужно вычислить значение x = (1+2)*(3+4)*(5+6).. Главный компьютер может разбить это на более мелкие вычисления (1+2), (3+4), (5+6) и отправить их на три разных компьютера. Эти три компьютера могут выполнять вычисления и отправлять результат на ведущий компьютер. На всех трех компьютерах одновременно будет запущена одна и та же программа сложения, но с разными данными.

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

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

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

Транслировать

Трансляция - это стандартная коллективная операция. В широковещательном режиме один процесс отправляет одни и те же данные всем другим процессам. Основное использование широковещательной передачи в параллельном программировании - отправка пользовательских данных или параметров конфигурации всем процессам.

communicator - Группа процессов, которые могут взаимодействовать друг с другом. В нем есть весь контекст, необходимый для связи с другими процессами в группе.

Все коллективные операции должны вызываться всеми процессами коммуникатора.

Все процессы корневого процесса и получателя вызывают MPI_Bcast, но выполняют разные функции. Корневой процесс заполнит буфер данными и отправит их всем процессам. Процессы-получатели будут получать данные в своем локальном буфере данных.

data - Буфер данных с данными для отправки или получения

count— Количество единиц типа данных для отправки. Если процессу нужно отправить одно целое число, счет будет 1

datatype - Тип данных. Пример: MPI_INT

root - ранг корневого процесса, отправляющего данные

MPI_Bcast - это не простая операция множественной отправки. В MPI_Bcast каждый процесс-получатель снова становится корневым и помогает транслировать данные остальным процессам - аналогично протоколу сплетен.

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

Разброс

Scatter похож на широковещательную рассылку, но вместо того, чтобы отправлять одни и те же данные всем процессам, он отправляет разные данные.

send_data— массив данных, который необходимо отправить

send_count - количество данных, которые будут отправлены для каждого процесса

Если send_count равно 2, корневой процесс отправит первые две записи в массиве send_data первому процессу, две вторые записи - второму процессу, а затем продолжит. Для простоты предположим, что массив можно разделить поровну.

Собирать

Операция сбора собирает данные со всех процессов и собирает их в корневой процесс.

Элементы упорядочены по рангу процесса-получателя в recv_dataarray.

recv_data будет пустым для всех процессов, кроме root. recv_count - это количество элементов, полученных каждым процессом, а не общая сумма счетчиков от всех процессов.

Уменьшать

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

send_data— массив элементов, каждый процесс хочет уменьшить

recv_data - применимо только к корневому процессу. После операции в нем содержатся уменьшенные результаты с размером счета.

op - операция, которую нужно применить к данным

Сокращение происходит для каждой записи массива. Если каждый процесс отправляет массив из одного элемента с op sum, результирующий массив будет содержать один элемент со всеми добавленными элементами array [0] th. Если каждый процесс отправляет два элемента, в массиве результатов будет два элемента - result [0] = сумма всех arr [0] ’s и result [1] = сумма всех arr [1]’ s.

Все вышеуказанные операции относятся к числу операций от одной до многих. Давайте рассмотрим несколько операций.

AllGather

Операция AllGather собирает данные со всех процессов и собирает их для всех процессов.

Подобно MPI_gather, но не имеет корневого процесса, собранные данные должны быть отправлены всем процессам.

AllReduce

AllReduce уменьшит значения и отправит результат всем процессам.

То же, что и reduce, но без корневого процесса, так как данные нужно отправлять всем процессам.

Все на все

AlltoAll - это комбинация разброса и сбора. Каждый процесс отправляет одинаковый объем данных всем другим процессам и получает одинаковое количество данных от каждого процесса. Таким образом, это всего 2n операций, включая его самого.

send_count - количество элементов для отправки каждому процессу

recv_count - количество элементов для получения от каждого процесса

Допустим, у нас есть три процесса, и каждый процесс имеет массив из 6 элементов assend_data. Каждый процесс отправит два элемента каждому процессу в порядке ранга и получит два элемента от каждого процесса, которые будут помещены в его локальный recv_data, упорядоченный по рангу.

AlltoAllv

AlltoAllv векторизован alltoall. В alltoall все процессы отправляют одинаковый объем данных и получают одинаковый объем данных. В alltoallv процесс может отправлять разные подсчеты разным процессам.

send_counts - Целочисленный массив, где запись i указывает количество элементов для отправки в ранг i

send_disp - Целочисленный массив, где запись i указывает смещение (смещение от send_data в единицах send_datatype), откуда данные должны быть отправлены в ранг i

recv_counts— Целочисленный массив, где запись j указывает количество элементов, которые нужно получить из ранга j.

recv_disp— Целочисленный массив, где запись j указывает смещение (смещение от recv_data в единицах recv_datatype), куда должны быть записаны данные из ранга j.

Данные, которые необходимо отправить конкретному процессу, можно идентифицировать по массиву смещений отправки и массиву счетчиков отправки. Каждый процесс также может получать объем данных, отличный от отправленного, который будет помещен в recv_data. Где разместить в recv_data, будет решено на основании смещений recv и счетчиков recv.

Как и alltoallv, есть векторизованные версии scatter (allscatterv), gather (allgatherv) и reduce (allreducev).

Простой пример

Давайте рассмотрим, как мы можем использовать эти операции на примере, который мы видели в начале статьи.

Compute x = (1+2)*(3+4)*(5+6)

У нас есть всего 3 процесса в коммуникаторе MPI_COMM_WORLD, и все они запускают одну и ту же вышеуказанную программу. Процесс с id = 0 является корневым процессом.

Строка 10 - корневой процесс имеет входные данные и заполняетsend_data этими входными данными, чтобы отправить их всем процессам.

Строка 12 - Каждый процесс получит два элемента из массива в порядке их ранга. Процесс с рангом 0 получит первые два элемента, процесс с рангом один получит следующие два элемента, а процесс с рангом три получит последние два элемента.

Строка 14 - Все процессы будут вызывать MPI_Scatter. Корневой процесс отправит данные, а также получит два своих элемента в recv_data. Все остальные процессы получат два элемента в своих recv_data.

Строка 15 - Все процессы будут выполнять сложение своих recv_data.

Строка 22 - MPI_Reduce уменьшит массив всех сумм от всех процессов, использующих операцию продукта, и результат операции будет отправлен корневому процессу в result буфере.

Строка 23 - корневой процесс распечатает результат общих вычислений.

Заключение

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

Спасибо за прочтение. Я надеюсь, что это было полезно. Обязательно ознакомьтесь с моей предыдущей статьей об интерфейсе передачи сообщений.



Следите за новостями, чтобы увидеть больше статей в этой теме.

использованная литература

[1] Уильям Гропп, Коллективная коммуникация и вычисления в MPI, https://wgropp.cs.illinois.edu/courses/cs598-s15/lectures/lecture29.pdf