Введение в коллективное общение
Всегда нужно делать что-то быстро. С увеличением объема данных часто один компьютер не справляется с этой задачей. Именно здесь на помощь приходят параллельные вычисления. Проще говоря, параллельные вычисления разбивают сложную задачу на более мелкие части и выполняют эти более мелкие части одновременно на разных подключенных компьютерах. Таким образом, мы используем несколько вычислительных ресурсов одновременно для решения вычислительной задачи.
Например, нам нужно вычислить значение 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_data
array.
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