УДК 004.021
Алгоритм широковещательной передачи стандарта MPI на базе разделяемой памяти многопроцессорных NUMA-узлов
М. Г. Курносов, Е. И. Токмашева1
Предложен алгоритм реализации операции MPI_Bcast широковещательной передачи через разделяемую память многопроцессорных NUMA-узлов. Алгоритм основан на создании системы очередей в сегменте разделяемой памяти, через которые корневой процесс выполняет конвейерную передачу фрагментов сообщения (pipelining). Для сокращения накладных расходов на копирование между NUMA-узлами выполняется динамический анализ топологии MPI-коммуникатора и размещение очередей в памяти локальных NUMA-узлов процессов. Выполнен теоретический и экспериментальный анализ эффективности алгоритма. В рамках рассматриваемой модели найдены оптимальные значения размера фрагмента и длины очереди. Алгоритм реализован авторами на базе Open MPI и в среднем на 20-60 % сокращает время выполнения операции MPI_Bcast по сравнению с алгоритмами в библиотеках MVAPICH и Open MPI (coll/sm).
Ключевые слова: Bcast, broadcast, MPI, NUMA, коллективные операции, параллельное программирование, вычислительные системы.
1. Введение
Широковещательная передача стандарта MPI реализуется операцией MPi_Bcast, которая выполняет передачу сообщения из памяти заданного корневого процесса в память остальных. Для значительного числа параллельных алгоритмов и пакетов суперкомпьютерного моделирования производительность (время выполнения) рассматриваемой операции является критически важной. Активно ведутся работы по созданию эффективных алгоритмов реализации MPi_Bcast на прикладном уровне протокола MPI (на базе операций send/recv) [1-4], на канальном уровне с учетом архитектуры конкретных коммуникационных технологий (InfiniBand, 100 Gigabit Ethernet), а также на физическом уровне в виде специализированных сетей и технологий (IBM BlueGene Tree Network, Cray Aries, Fujitsu Tofu, Mel-lanox InfiniBand SHARP).
В центре внимания данной работы - алгоритмы реализации MPl_Bcast в пределах одного многопроцессорного вычислительного узла. Такие алгоритмы применяются, если MPI-программа запущена на одном узле либо в составе иерархических (структурно-ориентированных) алгоритмов, которые выполняют два шага: 1) широковещательная рассылка из корневого процесса процессам-лидерам узлов; 2) широковещательная рассылка в пределах каждого узла.
В MPI-библиотеках MVAPICH, MPICH, Open MPI, Intel MPI такие алгоритмы коллективных операций получили название "shared memory-based", "SMP/NUMA-aware", "SHM-based".
1 Работа выполнена при поддержке РФФИ (проект № 18-07-00624).
2. Постановка задачи
Имеется вычислительный узел с неоднородным доступом процессоров к оперативной памяти (NUMA-система). Память системы разбита на NUMA-узлы, по которым распределены процессоры и их ядра. Обращение процессорного ядра к памяти своего NUMA-узла занимает меньше времени по сравнению с временем доступа к памяти другого (удаленного) NUMA-узла [5]. Как правило, каждый NUMA-узел имеет выделенный контроллер доступа к его локальной памяти, а доступ к памяти удаленных NUMA-узлов осуществляется через межпроцессорный канал связи (например, Intel UPI/QPI, HyperTransport, IBM X-BUS, Huawei HCCS, Cavium CCPI).
На вычислительном узле запущена MPI-программа, p процессов которой распределены системой управления ресурсами по процессорным ядрам NUMA-узлов и образуют отдельный MPI-коммуникатор comm.. По умолчанию MPI-библиотеки (Open MPI, MVAPICH, Intel MPI) привязывают процесс к ядру, на котором он запущен. Это предотвращает его возможный перенос операционной системой на другое ядро. В соответствии с семантикой операции MPI_Bcast необходимо передать count элементов типа datatype из буфера buf в адресном пространстве процесса root в буферы buf некорневых процессов коммуникатора comm:
MPI Bcast(buf, count, datatype, root, comm).
Все процессы должны передать в операцию одинаковый размер сообщения m = count ■ sizeof(datatype), однако count и datatype в процессах могут иметь разные значения (например, передается строка матрицы, а принимается столбец - тип данных с регулярным шагом между элементами). Заметим, что буфер buf пользователя может находиться в памяти удаленного NUMA-узла по отношению к текущему процессу. Такая ситуация возникает при назначении процессам политики выделения памяти с фиксированных NUMA-узлов либо при недостаточном объеме памяти на локальном NUMA-узле. Стандарт MPI не допускает одновременного (многопоточного) выполнения нескольких коллективных операций на одном коммуникаторе.
Необходимо предложить алгоритм реализации операции MPI_Bcast, удовлетворяющий требованиям стандарта MPI и использующий разделяемую память вычислительного узла для сокращения времени передачи информации.
3. Обзор работ и проектов
В основе большинства известных авторам алгоритмов операций MPI_Bcast для систем с общей памятью лежит двухэтапная процедура [6-10]: 1) при формировании нового MPI-коммуникатора создается сегмент разделяемой памяти и система очередей в нем; 2) при каждом обращении к операции MPI_Bcast корневой процесс выполняет конвейеризацию передачи сообщения (pipelining) - разбивает его на фрагменты фиксированной длины и последовательно копирует их в буферы разделяемой очереди (фаза copy-in). Некорневые процессы ожидают завершения записи очередного фрагмента корнем и копируют его в буфер пользователя (фаза copy-out). При достижении конца очереди корневой процесс ожидает завершения копирования фрагментов некорневыми процессами и начинает повторное использование буферов. Такой подход получил название copy-in/copy-out (CICO) и широко используется на практике, так как обеспечивает переносимость и не требует наличия дополнительных библиотек и функциональных возможностей ядра операционной системы. К факторам, ограничивающим масштабируемость CICO-алгоритмов, в первую очередь относятся двойное копирование каждого фрагмента, синхронизация процессов при записи буферов (уведомления о их готовности), а также синхронизация при заполнении всей очереди. Наряду с CICO-методом во многих MPI-библиотеках реализован подход, устраняющий необходимость двойного копирования за счет использования расширений ядра операционной системы,
обеспечивающих возможность одному процессу напрямую копировать фрагмент из своего адресного пространства в память другого процесса. За таким подходом закрепилось название "zero copy" - отсутствие дополнительных копирований. Его безусловное применение ограничено необходимостью использования нестандартных модулей ядра операционной системы и/или расширенных прав/привилегий для процесса пользователя. Примерами могут служить проекты KNEM, XPMEM, CMA. В данной работе рассматриваются алгоритмы класса CICO.
В библиотеке MVAPICH [8] процессы одного узла объединяются в коммуникатор и создают сегмент разделяемой памяти, в котором для каждого процесса создается циклическая очередь из w слотов. Каждый слот содержит буфер для хранения фрагмента длины f байт, а также номер psn операции, на которой слот был заполнен. Номер psn используется корневым процессом для уведомления остальных о готовности данных в буфере. По умолчанию длина очереди w = 128 слотов, а каждый буфер занимает f = 8192 байт. При вызове операции MPi_Bcast корневой процесс root разбивает исходное сообщение на фрагменты по f байт. Очередной фрагмент копируется в буфер слота c номером write % w, а поле psn слота устанавливается в значение счетчика write. Некорневые процессы ожидают готовности данных в буфере корня - ждут пока корень не установит поле psn слота read % w в значение read. После чего копирует фрагмент в буфер пользователя. Перед переходом к следующему фрагменту счетчики write и read всех процессов увеличиваются на единицу. Если все слоты заполнены, осуществляется барьерная синхронизация процессов и слоты начинаются заполняться с начала очереди. Размер сегмента разделяемой памяти имеет порядок O(pwf), а для хранения указателей на слоты каждому процессу требуется порядка O(pw) байт памяти. Топология NUMA-системы при размещении очередей не учитывается, а уведомление процессов осуществляется по плоскому дереву за время O(p).
В работе [9] предложен алгоритм, основанный на использовании p циклических очередей в разделяемой памяти. Он реализован в компоненте coll/sm библиотеки Open MPI. Для каждого процесса создается очередь из w буферов по f байт (по умолчанию f = 8192), а также w блоков с управляющей информацией длиной c байт каждый. Корневой процесс root разбивает исходное сообщение на фрагменты по f байт и копирует очередной фрагмент в следующий свободный буфер своей очереди. Корень уведомляет каждый дочерний процесс о готовности буфера k путем записи в управляющий блок k дочернего процесса размера скопированного фрагмента. Некорневые процессы ожидают, пока корневой процесс не запишет в их управляющий блок размер ожидаемого сегмента, после этого они копируют сообщение в буфер пользователя. Если все буферы заполнены, процессы выполняют барьерную синхронизацию. Для минимизации накладных расходов на барьерную синхронизацию очередь разбивается на два множества буферов (banks). При исчерпании буферов первого множества запускается барьерная синхронизация, а корневой процесс без блокировки начинает использовать следующее множество. Процессы логически организованы в завершенное k-арное дерево (complete k-ary tree) с корнем в root, оно используется для взаимного уведомления процессов о готовности буфера в очереди процесса root. Общий размер сегмента разделяемой памяти имеет порядок O(pwf + pc), для хранения указателей на буферы и дерева каждому процессу требуется порядка O(w + pk) байт памяти.
В работе [10] предлагается использовать одну циклическую очередь из w буферов по f байт для всех процессов (авторы использовали 4 буфера по 8192 байт). Корневой процесс копирует фрагмент в следующий свободный буфер очереди и уведомляет об этом процессы (шаг release). Каждый некорневой процесс ожидает уведомления о готовности очередного фрагмента, после этого копирует его в буфер пользователя. Для повторного использования буферов корневой процесс ожидает уведомления от остальных процессов (шаг gather). Взаимное уведомление процессов о готовности данных в буфере и его освобождении реализуется через две таблицы счетчиков в сегменте разделяемой памяти sh_release_flag[rank] и sh_gather_flag[rank]. Для сокращения времени синхронизации на шагах release и gather процессы логически выстраиваются в дерево, корнем которого является процесс root. Корневой
процесс уведомляет дочерние о готовности данных в буфере путем записи в их флаги sh_release_flag\i\ номера его шага release, в свою очередь, дочерние процессы ожидают, пока значение их флага sh_release_flag[rank\ не станет равно номеру их шага release. Аналогично на фазе gather дочерние процессы уведомляют родительский процесс путем записи в свой счетчик sh_gather_flag[rank\ номера шага gather, а родительский процесс ожидает, пока все дочерние процессы установят свои флаги sh_gather_flag. Процессы, выполняющиеся на ядрах одного процессора, образуют отдельное дерево для реализаций уведомлений и более эффективного использования кеш-памяти. Корни деревьев всех процессоров вычислительного узла становятся дочерними вершинами корневого процесса, который является корнем в дереве своего процессора. Размер сегмента разделяемой памяти имеет порядок O(p + wf). Алгоритм не имеет реализации с открытом кодом и не учитывает топологию NUMA-системы при размещении очередей в памяти.
Предложенный в данной работе алгоритм, в отличие от известных, учитывает топологию NUMA-системы при размещении очередей в сегменте разделяемой памяти, уведомление процессов о готовности буферов реализовано деревьями различных видов.
4. Описание алгоритма
Разработанный алгоритм включает две стадии: 1) формирование при создании нового коммуникатора сегмента разделяемой памяти и системы очередей в нем; 2) выполнение конвейеризированной передачи сообщения при каждом вызове операции МР1_ВсаБ-Ь.
4.1. Создание сегмента разделяемой памяти
При создании нового МР1-коммуникатора (включая MPI_C0MM_W0RLD) все процессы формируют сегмент разделяемой памяти. Для этого используется РОБГХ-совместимый системный вызов ттар. На рис. 1 показан пример размещения информационных блоков в сегменте разделяемой памяти для p = 8 процессов, запущенных на ядрах двух МиМЛ-узлов. Размеры всего сегмента и отдельных блоков кратны размеру страницы памяти. Это продиктовано тем, что управление привязкой областей памяти к МиМЛ-узлам осуществляется на уровне страниц.
Блок Содержание Размер Процесс Размещение страниц (NUMA-узел)
leaders[p] 0, 1, 0, 1, 0, 1, 0, 1 4KB 0 0
shm_op shm nreaders 1 4KB 0 0
0
shm_op shm_nreaders 1 4KB 0 0
0
shm_controls[s] ......... 8 • 4KB 0 0
shm_fragments[s] ......... 8 • 8KB
shm_controls[s] ......... 8 • 4KB 1 1
shm_fragments[s] ......... 8 • 8KB
shm_controls[s] ......... 8 • 4KB 2 0
shm_fragments[s] ......... 8 • 8KB
shm_controls[s] ......... 8 • 4KB 7 1
shm_fragments[s] ......... 8 • 8KB
NUMA-узел 0 NUMA-узел 1
© ©! !©©
© ©!
Рис. 1. Структура сегмента разделяемой памяти (после инициализации): p = 8 процессов на двух МиМЛ-узлах; размер страницы памяти - 4 КВ, каждая очередь содержит ^ = 8 буферов по f = 8 КВ каждый и разбита на q = 2 множества (суммарный размер сегмента 780 КВ)
В начале сегмента размещается целочисленный массив leaders из p элементов. Он используется для определения процессами своего расположения в пределах вычислительного узла. В ячейке leaders[i] хранится номер процесса, являющегося лидером NUMA-узла процесса i. Под массив выделяется минимально необходимое число страниц памяти (ceil(p • sizeof(int) / pagesize)). Например, при p = 1024 процессах для хранения массива leaders на узле с архитектурой x86-64 достаточно одной страницы памяти размером 4096 байт. Далее следуют q страниц, содержащих по два счетчика shm_op и shm_nreaders. Они используются для синхронизации процессов в ходе выполнения операции MPI_Bcast. Адреса счетчиков выравнены на границу размера строки кеш-памяти для минимизации вероятности возникновения ложного разделения данных (false sharing) при их одновременном использовании процессами. Далее для каждого из p процессов размещается циклическая очередь из s буферов длиной f байт, а также массив из s управляющих блоков для хранения информации о состоянии буферов. Каждый буфер и управляющий блок занимают минимально необходимое число страниц памяти. В общем случае размер сегмента линейно зависит от числа p процессов и длин s очередей и занимает w + wq + p-s(w + f) байт памяти, где w - размер страницы. На практике длину s и размер f буфера очереди следует выбирать с учетом доступного объема оперативной памяти. Например, при p = 64 процессах на узле и s = 1024, f = 8192 сегмент памяти будет занимать 384 MB.
После вызова mmap каждый процесс инициализирует области сегмента с его структурами данных: обнуляет управляющие блоки и первый байт каждой страницы всех буферов очереди. Это обеспечивает выделение физических страниц памяти с его локального NUMA-узла (использование политики first touch policy ядра операционной системы GNU/Linux). Массив leaders и страницы памяти со счетчиками shm_op и shm_nreaders инициализируют процесс 0. Как отмечено выше, при первом обращении к любому адресу addr сегмента происходит выделение физической страницы памяти с локального NUMA-узла процесса и с опережением с этого же NUMA-узла выделяется фиксированный блок страниц для следующих за addr адресов. Это выполняется службой кеширования страниц операционной системы (page cache readahead). Последнее может привести к тому, что при инициализации процессом 0 своего блока данных, будут с опережением выделены страницы c его локального NUMA-узла для следующих за ним в сегменте разделяемой памяти блоков данных. Однако эти блоки данных могут принадлежать процессам (1, 2, ..., p - 1), находящимся на других NUMA-узлах. Для установления корректной привязки страниц памяти к NUMA-узлам сразу после вызова mmap на время инициализации сегмента вызовом mad-vise(seg, segsize, madv_random) отключается опережающее чтение страниц памяти.
Далее каждый процесс формирует в своей памяти фрагмент дерева для выполнения уведомлений процессов. Каждому процессу достаточно знать номера родительского и дочерних процессов. На этапе создания коммуникатора неизвестен номер корневого процесса операции MPI_Bcast. Поэтому каждый процесс формирует часть дерева для всех p возможных вариантов выбора корневого процесса. На хранение дерева в каждом процессе требуется порядка O(p) байт памяти. При вызове операции MPI_Bcast действительные номера родительского и дочерних процессов для заданного значения root вычисляются за константное время. Реализована поддержка k-арных деревьев (k-ary tree), k-номиальных деревьев (k-nomial tree), плоского дерева (flat tree) и цепочки (chain, linear tree). Время инициализации сегмента линейно зависит от числа p процессов, длины s очереди и количества q множеств.
4.2. Алгоритм операции MPI_Bcast
При вызове функции MPI_Bcast процессы коммуникатора получают номер root корневого процесса, размер m сообщения и буфер передачи/приема сообщения. На рис. 2 приведен псевдокод алгоритма, в котором: p - число процессов, rank - номер процесса, s - число бу-
феров очереди; q - количество множеств; op - счетчик операций синхронизации; shm_readers[k] - число процессов, использующих множество k; shm_op[k] - номер операции множества k; get_next_fragment() - ссылка на следующий фрагмент сообщения; shm_buffers[rank][index] - адрес буфера index очереди процесса rank; shm_controls[rank][index] - адрес управляющего блока index процесса rank. Переменные с префиксом shm_ указывают на объекты в сегменте разделяемой памяти.
function MPI_Bcast(buf, count, datatype, root, comm)
Input: shm_op[k] == 1, shm_nreaders[k] == 0, shm_controls[i][j] == 0, op = 0
msgsize = count * sizeof(datatype) while sentsize < msgsize do
fragment = get_next_fragment(buf, sentsize) // Корневой процесс if rank == root then set = op % q op = op + 1
// Ожидание освобождения буферов множества set wait_for(shm_nreaders[set] == 0) shm_nreaders[set] = p - 1 shm_op[set] = op - 1
index = set * (s / q);
while index < (set + 1) * (s / q) and sentsize < msgsize do // Копирование фрагмента в буфер index очереди процесса root copy(fragment, shm_buffers[root][index], fragment_size) // Уведомление дочерних процессов root о готовности буфера index for i = 0 to tree[root].nchilds - 1 do child_rank = tree[root].childs[i] shm_controls[child_rank][index] = fragment_size end for
sentsize = sentsize + fragment_size index = index + 1
end while else
// Листовые и внутренние процессы // Ожидание готовности буферов множества set set = op % q
wait_for(shm_op[set] == op) op = op + 1
index = set * (s / q);
while index < (set + 1) * (s / q) and sentsize < msgsize do
// Ожидание готовности буфера index в очереди родительского процесса wait_for(shm_controls[rank][index] > 0) fragment_size = shm_controls[rank][index] shm_controls[rank][index] = 0 if tree[root].nchilds > 0 then
// Уведомление дочерних процессов о готовности буфера index for i = 0 to tree[root].nchilds - 1 do child_rank = tree[root].childs[i] shm_controls[child_rank][index] = fragment_size end for end if
// Копирование из буфера index процесса root в буфер пользователя copy(shm_buffers[root][index], fragment, fragment_size) sentsize = sentsize + fragment_size index = index + 1
end while
// Атомарное уменьшение на 1 atomic_dec(shm_nreaders[set])
end if end while end function
Рис. 2. Псевдокод алгоритма операции MPI Bcast
Корневой процесс root реализует конвейерную передачу [m//] фрагментов сообщения, каждый длиной f байт (последний фрагмент может иметь размер m % f байт). Текущий фрагмент копируется в следующий свободный буфер index очереди корневого процесса, а в управляющий блок index каждого дочернего процесса заносится размер фрагмента, тем самым корень уведомляет дочерние процессы о готовности данных. Если все s буферов заполнены, корневой процесс выполняет барьерную синхронизацию - ожидает, пока все процессы закончат использование его буферов. Барьерная синхронизация в корне реализуется на ожидании равенства нулю глобального счетчика shm_nreaders текущего множества set. После чего корень устанавливает глобальный счетчик операций shm_op[set] в значение op - 1, ожидание которого выполняют некорневые процессы. Для сокращения издержек на барьерную синхронизацию в алгоритме реализовано совмещение ожидания освобождения буферов и переход к использованию корневым процессом следующей части элементов очереди [9]. Для этого все s буферов каждого процесса разбиты на q множеств. При заполнении s / q буферов множества j корневой процесс переходит к использованию буферов следующего множества (j + 1) % q. Например, при сообщении длиной 12 фрагментов и очереди из s = 8 буферов, разбитых на q = 2 множеств, корневой процесс заполняет фрагментами первые 4 буфера, уведомляя каждый раз дочерние процессы, и переходит к заполнению следующих 4 буферов, которые еще не использовались. Последние 4 фрагмента сообщения корневой процесс начнет записывать в первые 4 буфера, которые к этому времени с большой вероятностью будут освобождены остальными процессами.
Листовые процессы ожидают уведомления от родительского процесса о готовности очередного фрагмента и копируют его из очереди родителя в буфер пользователя.
Некорневые внутренние процессы ожидают уведомления от родительского процесса и уведомляют свои дочерние процессы. После чего копируют фрагмент из очереди корня в буфер пользователя.
Алгоритм реализован на базе библиотеки Open MPI 4.0.0 в виде отдельного компонента подсистемы coll. Операция wait_for реализована активным ожиданием в цикле с периодическим вызовом функции opal_progress () «продвижения» выполнения коммуникационных операций (в частности неблокирующих, non-blocking). Копирование фрагментов реализовано функциями подсистемы Open MPI OPAL упаковки и распаковки буферов в памяти с поддержкой производных типов данных. Для предотвращения некорректного уведомления процессами друг друга, в силу возможного внеочередного выполнения инструкций процессором, после записи в буферы очередей и управляющие блоки вызывается операция барьера записи памяти (write memory barrier). Архитектурно-зависимые функции используются из подсистемы Open MPI OPAL (Open Portable Access Layer). После создания сегмента разделяемой памяти и инициализации процессами своих структур данных выполняется проверка корректности выделения страниц памяти с NUMA-узлов. Каждый процесс, используя системный вызов move_pages(), определяет соответствие номера своего NUMA-узла и NUMA-узла, с которого выделены страницы памяти для буферов его очереди и управляющих блоков.
5. Анализ эффективности алгоритма
Сложность алгоритма по памяти определяется накладными расходами на создание сегмента разделяемой памяти, размер которого линейно зависит от числа p процессов и длин s очередей и занимает порядка O(p-s(w + f)) байт, где w - размер страницы.
5.1. Организация экспериментов
Экспериментальная часть работы проводились на серверах двух конфигураций:
1) двухпроцессорный сервер Intel Xeon Broadwell: 2 x Intel Xeon E5-2620 v4 (8 ядер, Hy-perThreading отключен, кеш-память L1 32 KB, L2 256 KB, L3 20 MB); RAM 64 GB (2 NUMA-узла); ядро linux 4.18.0-80.11.2.el8_0.x86_64 (CentOS), gcc 8.2.1;
2) двухпроцессорный сервер Intel Xeon Nehalem: 2 x Intel Xeon E5620 (4 ядра, Hyper-Threading отключен, кеш-память L1 256 KB, L2 1 MB, L3 12 MB); RAM 24 GB (2 NUMA-узла); ядро linux 4.16.3-301.x86_64 (Fedora), gcc 8.2.1.
В экспериментах использовался пакет MVAPICH 2.3.2 (параметры конфигурирования: CFLAGS=-O3 CXXFLAGS=-O3 —with-device=ch3:mrail —with-rdma=gen2).
Использовалась master-ветвь пакета Open MPI 4 (параметры сборки: --with-platform=contrib/platform/mellanox/optimized).
Пакет Intel MPI Benchmarks 2019 Update 2 (IMB) собирался в конфигурации по умолчанию. В работе использовалась распространенная методика оценки эффективности коллективных операций [10]. Для каждого размера сообщения операция MPI_Bcast запускалась от 5000 до 15 раз. Для каждого вызова MPI_Bcast «отключалось» использование кеш-памяти (на каждом вызове использовался новый буфер), корнем операции выбирался процесс 0. После каждого обращения к MPI_Bcast выполнялась барьерная синхронизация встроенным алгоритмом IMB. Параметры запуска теста:
IMB-MPI1 Bcast -off_cache 20,64 -iter 5000,250 -msglog 6:24 -sync 1 -imb barrier 1 -root shift 0 -zero size 0
В каждом эксперименте тест IMB запускался 5 раз, для каждого размера сообщения отбрасывались минимальное и максимальное значения t_max, далее значение t_max усреднялось по результатам трех запусков.
5.2. Оценка времени выполнения алгоритма
В общем случае наибольшим временем выполнения алгоритма характеризуются листовые процессы дерева, по которому распространяется уведомление о готовности буфера. На рис. 3 показаны временные диаграммы выполнения шагов алгоритма корневым (root) и листовым (leaf) процессами для разных параметров очередей.
а)
root |msso| c1 n ms |so| c2 n ms 3^1 c3 n ms iso| c4 n
leaf wo
M
C1 A WO W c2 A MO
M
s = 1
C3 A WO w c4 I a
б)
root |m3so| c1 n c2 In c3 n c4 In ms |so|c5 n c6 In c7 In c8 In s = 4, q = 1
leaf wo
M
C1 w c2 w c3 \w\ C4 \awo w c5 w c6 W c7 M C8 a
в)
root |msso| c1 in C2 in c3 in c4 |N|mSS^| c5 in C6 in c7 in c8 |n
s = 8, q = 2
leaf wo
w
C1 I M C2 I M C3 I M C4 A WO M C5 I M C6 I M C7 I M C8 IA
t
Рис. 3. Временные диаграммы выполнения шагов алгоритма корневым (root) и листовым (leaf) процессами (WS - операция ожидания освобождения множества, SO - установка счетчика shm_op, Ck - копирование фрагмента k, N - уведомление дочернего процесса о готовности буфера, WO - ожидание готовности буферов множества, W - ожидание уведомления о готовности буфера, A - атомарное уменьшение счетчика shm_nreaders): а) очередь из s = 1 буфера, m = 4f; б) очередь из s = 4 буферов и одного множества (q = 1), m = 8f; в) очередь из s = 8 буферов, разбитых на два множества
(q = 2), m = 8f
Очередь из одного буфера. При использовании очереди из одного буфера (5 = 1, рис. 3а) конвейеризация передачи фрагментов сообщения невозможна. Корневой и листовой процессы выполняют по \т/копирований фрагментов. На первом шаге корневой процесс выполняет ожидание готовности множества (шаг ЖЗ), его единственный буфер может быть частично занят процессами, которые заканчивают выполнение предыдущего вызова MPI_Bcast. Далее он уведомляет (ЗО) листовой процесс о начале операции и запускает цикл копирования фрагментов в буфер. Листовой процесс ожидает в течение времени готовности множества буферов (ЖО). Затем начинает цикл копирования фрагментов из очереди корневого процесса. Уведомление о готовности первого буфера листовой процесс получает не раньше, чем корень завершит его копирование (С1) за время Ьс. На получение уведомления от корневого процесса (Ж) требуется единиц времени, оно зависит от структуры дерева операции Бсаз1 и, как следствие, от числа р процессов. Таким образом, копирование первого фрагмента листовым процессом завершится в момент времени + Ьс + + Ьс. После копирования лист уведомляет корень атомарной операцией (А) за время ЬА об освобождении буфера. Корневой процесс начинает повторно заполнять буфер. Общее время работы алгоритма с очередью из одного буфера не превышает значения
\т//](1№0 + 1С + 1А) + \т//](1№ + 1С).
Первое слагаемое включает накладные расходы на ожидание освобождения буферов и уведомление корневого процесса, второе - время получения уведомлений и копирования фрагментов. В полученном выражении не учтено, что последний фрагмент сообщения может быть меньше размера буфера. В любом случае время ¿с копирования фрагментов сообщения есть тХ, где t - время чтение/записи одного байта памяти. Тогда время ¿(т) работы алгоритма с очередью из одного буфера примет вид
1(т) = \т/^(1„0 + ЬА) + + 2т1.
Очередь из ж буферов. При использовании очереди из нескольких буферов и размере т сообщения больше f байт включается механизм конвейеризации (5 > 1, рис. 36). Наличие нескольких буферов позволяет корневому процессу без ожидания записывать 5 фрагментов в очередь. При этом запись корнем фрагмента к выполняется одновременно с копированием листовым процессом фрагмента к - 1. После заполнения всех 5 буферов корневой процесс ожидает завершения копирования всеми процессами, на что требуется не менее Ьс + ЬА единиц времени (второй шаг ЖЗ на рис. 3б). В общем случае синхронизация при заполнении всей очереди выполняется \т/Ь] раз, где Ь = / • 5 - суммарный размер буферов очереди. Время ¿(т, 5) выполнения алгоритма с очередью из 5 буферов
1(т,з) = \т/Ь](1:Ш0 + гс + ЬА) + + т!.
Из выражения ¿(т, 5) видно, что по сравнению с одним буфером потери на синхронизацию (первое слагаемое) в 5 раз меньше. При нулевых накладных расходах на ожидание (ЖО, Ж, А) очередь из 5 буферов обеспечивает сокращение времени Бca5t до двух раз: Ь(т)/ Ь(т, я) < 2. На практике отношение может иметь значение больше двух в силу того, что время ¿ж ожидания зависит от вида дерева операции Бca5t, а на время выполнения влияет распределение процессов по процессорным ядрам и копирование фрагментов с локальных/удаленных МиМЛ-узлов.
Очередь, разбитая на q множеств. Как отмечено выше, после заполнения всех буферов (при т > Ь) для их повторного использования корневой процесс ожидает в течение времени Ьс + ЬА уведомления от дочерних процессов. Ожидание можно сократить на время Ьс, если
разбить 5 буферов на два множества (рис. 3в). Это позволит корневому процессу без ожидания начать заполнять буферы следующего множества, пока дочерние процессы завершают копирование фрагментов из буферов предыдущего [9]. Время выполнения алгоритма с очередью из 5 буферов, разбитых на q множеств:
— ц\т/Ь](1„0 + ЬА) + гс + + т!.
Очевидно, что очереди из нескольких множеств целесообразно применять для сообщений т > Ь.
Для заданного размера т сообщения возможны следующие три случая (режима) работы очереди из 5 буферов.
1. При т. < / конвейеризация передачи сообщения не используется. В этом случае время работы алгоритма
— ^ШО + ^А + +
2. При / < т < Ь включается конвейеризация передачи фрагментов сообщения, 5 буферов очереди достаточно для передачи всего сообщения (потери на ожидания минимальны), время работы алгоритма:
1(т, э) — + ЬА+ + тЬ.
3. При т > Ь буферы очереди используются многократно, суммарное время ожидания возрастает с увеличением т и убывает с ростом Ь. Время выполнения алгоритма:
1(т,з) — \т/Ь](1:Ш0 + гА+ + + т!.
5.3. Определение оптимальных параметров очереди
Практический интерес представляет задача нахождения оптимального размера f буферов и длины 5 очереди, которая помещается в Ь байт памяти и обеспечивает минимум времени выполнения операции MPI_Bcast. Например, на этапе запуска МР1-программы необходимо определить оптимальную конфигурацию очереди, которая помещается в 1 % от размера памяти, приходящийся на одно процессорное ядро. Пусть Ьс — t • /. Положим, что т делится без остатка на размер f буфера. Последнее допущение распространяется на слагаемое и позволяет отбросить знак функции округления, последнее не меняет характера функциональной зависимости от f. Запишем время выполнения алгоритма для очереди из 5 буферов и найдем оптимальные значения/* размера буфера и длины 5* очереди:
д1 2
Ь(т, з) — \т /Ь](Ь№0 + ЬА) + \т /Ь]/Ь + т // • + тЬ, — — - + \т /Ь]Ь — 0,
_ __дТ _
Г — Vш/\ш /ь] • - ^•гп/г, з* — Ь/р —
Поясним полученные значения. В выражении Ь^т^} два слагаемых зависят от размера f буфера: с ростом f линейно возрастает суммарное время \т/Ь^1 ожидания перехода к следующему множеству, а суммарное время ожидания уведомлений убывает обратно пропорционально f. На рис. 4 показано поведение кривых \т/Ь^1 и а также общего времени работы алгоритма.
t(m, s), мкс 17 300 . 17 200 [ [f 17 100 17 000 16 900 16 800 I-16 700
Размер /буфера, байт
Рис. 4. Зависимость времени t(m, s) работы алгоритма (левая ось), а также времени \m/b]tc и \m/f]tw (правая ось) от размераf буфера (параметры модели: m = 16 MB, b = 4 MB, s = blf, q = 1,
t = 10-9 с, two = 100i, tA = 10t tw = 50t)
Учитывая, что > Ь и страничное выделение памяти для очередей с локальных МиМЛ-узлов, практически целесообразным видится использование буферов размера f > округленного до ближайшего сверху числа, кратного размеру страницы памяти. В соответствии с этим в табл. 1 показан объем памяти, необходимый для очереди одного процесса, при заданном размере / буфера и длине 5 очереди. Зеленым цветом отмечены очереди, которые занимают не более 10 МВ памяти, что в среднем соответствует 0.1-1 % размера оперативной, приходящейся на одно процессорное ядро в современных ВС (Тор500, 2019 год).
Таблица 1. Размеры очереди при рекомендуемых сочетаниях параметров / и 5: зеленый цвет - очереди небольших размеров (Ь < 10 МВ); желтый цвет - 10 МВ < Ь <
100 МВ; красный цвет - Ь > 100 МВ
f, байт Размер b очереди, МВ
s=fl2 s=fl4 s=fl8 s=fl16 s= fl32 s=fl64 s= fl128 s=fl256 s=fl512
4096 8 4 2 1,0 0.5 0.3 0.1 0.1 0.03
8192 32 16 8 4 2 1 0.5 0.3 0.1
12288 72 36 18 9 5 2 1.1 0.6 0.3
16384 128 64 32 16 8 4 2 1 0.5
20480 200 100 50 25 13 6 3.1 1.6 0.8
24576 288 144 72 36 18 9 4.5 2.3 1.1
28672 392 196 98 49 25 12 6.1 3.1 1.5
32768 512 256 128 64 32 16 8 4 2
36864 648 324 162 81 41 20 10.1 5.1 2.5
40960 800 400 200 100 50 25 12.5 6.3 3.1
45056 968 484 242 121 61 30 15.1 7.6 3.8
49152 1152 576 288 144 72 36 18 9 4.5
53248 1352 676 338 169 85 42 21.1 10.6 5.3
57344 1568 784 392 196 98 49 24.5 12.3 6.1
61440 1800 900 450 225 113 56 28.1 14.1 7
65536 2048 1024 512 256 128 64 32 16 8
В условиях отсутствия полной информации о конфигурации многопроцессорного узла целевой ВС, предложенные выражения / размера буфера и длины 5* очереди обеспечивают хороший уровень производительности. На рис. 5 показаны зависимости времени выполнения алгоритма от размера / буфера для очереди, занимающей Ь = 4 МВ памяти. Минимальное
время достигнуто при размерах буфера 8 КВ и 12 КВ байт, что соответствуют значениям/* и
*
7
'о4 6.8 % 6.6
й 64
5 6.2 а « 6
5.8
1
Размер буфера (KB) m = 16 KB, p = 8
li
'—\ О 10.8
^ 10.6
5 10.4
S3 10?
a
m 10
9.8
N oo yf О О N г-i n ^ ао ON н
Размер буфера (KB)
m = 16 KB, p = 16
и л РЗ
13.5 13 12.5 12 11.5 11 10.5 10
3
Размер буфера (KB) m = 32 KB, p = 8
26 -25
У24
3-23
й 22
й 21
£<20 И 19
18
С<1 00 о ^о с-
m ^ ю » ft г-
Размер буфера (KB) m = 64 KB, p = 8
Размер буфера (KB)
= 64 KB, p = 816
Размер буфера (KB)
m = 32 KB, p = 16 m
Рис. 5. Зависимость времени выполнения алгоритма от размераf буфера на двух NUMA-узлах (p = 8 процессов) сервера Intel Xeon Nehalem и двух NUMA-узлах (p = 16 процессов) сервера Intel
Xeon Broadwell (b = 4 MB, s = b/f, q = 1, chain tree)
Рассмотрим способы выбора параметров алгоритма при других исходных данных.
1. Известен размер сообщения т (например, в результате анализа программы пользователя или получена оценка сверху размера сообщения). Выбрать f и 5. Наилучший вариант: взять f и 5 такие, что / < т < (сообщение полностью помещается в очередь). Для этого положим f — ^т и округлим полученное значение до ближайшего сверху числа, кратного размеру страницы памяти; 5 = т//
2. Задан фиксированный размер f фрагмента, выбрать длину 5 очереди. Пусть известна оценка сверху ттах размера сообщения. Для достижения режима минимальных потерь достаточно взять 5 — \штах//].
3. Известна длина 5 очереди, выбрать размер f буфера. Считаем, что известна оценка сверху ттах размера сообщения. Полагаем Ь — ттах и применяем ранее полученную формулУ: /— 4™тх.
5.4. Анализ зависимости времени алгоритма от параметров очереди
Из вида ^т, 5) следует, что время выполнения алгоритма линейно зависит от размера т сообщения, обратно пропорционально от длины 5 очереди и ее размера Ь (рис. 6).
t(m,s] ззооо
29 500 26 000 22 500 19 000 15 500 12 000
, мкс
t(m,s = 1)
х2!
mt
-^■счооою^-счооою-^-счс
I оо I о
Длина s очереди
Рис. 6. Зависимость времени t(m, s) выполнения алгоритма от длины s очереди (параметры модели: t = 10-9 c, m = 16 MB, f = 8192, q = 1, tw = 50t, two = 100t, tA = 10t)
На рис. 7 приведена зависимость времени выполнения MPI_Bcast от размера m сообщения для различных длин s очередей на сервере Intel Xeon Broadwell и Intel Xeon Haswell. Время нормализовано относительно времени алгоритма с очередью из одного буфера (s = 1, алгоритм без конвейеризации). При размере m сообщения, не превышающем длины f буфера, конвейеризация не задействуется, так как используется только один буфер. На рис. 7а такая ситуация прослеживается для сообщений, не превышающих размера буфера f = 8 КВ. При размере сообщения больше f байт передающий процесс может без ожидания записывать фрагменты в s/q буферов. Например (рис. 7а), очередь из s = 2 буферов по 8192 байт позволяет без ожидания передавать сообщения до 16 KB, что приводит к сокращению времени на 30 % относительно версии с одним буфером; очередь из s = 4 позволяет без ожидания передавать сообщения до 32 KB (сокращение времени на 40-46 %). Как отмечено выше, накладные расходы на синхронизацию процессов не позволяют достигнуть теоретически возможного ускорения в два раза, а значительные размеры сегмента разделяемой памяти ограничивают применение длинных очередей. Эксперименты показали эффективность очередей из 32-64 буферов.
Размер сообщения (байт)
а)
Размер сообщения (байт) б)
Рис. 7. Зависимость времени выполнения алгоритма от размера m сообщения для различных длин s очередей (время нормализовано относительно времени для s = 1; q = 1, f = 8192, p = 8, один NUMA-узел, flat tree): а) сервер Intel Xeon Broadwell; б) сервер Intel Xeon Haswell
При фиксированной длине 5 очереди зависимость времени г(ш, 5) от размера / буфера имеет нелинейный характер с локальными областями монотонного роста и убывания. Все слагаемые, зависящие от параметра /, убывают с его ростом. Исключение составляет общее время \m/(fs)]ft ожидания перехода к следующему множеству. Функция |х] наименьшего целого, большая или равная х, определяет «пилообразный» характер зависимости \т/Ь]/£ от / На рис. 8 показано время выполнения алгоритма для очереди из 5 = 32 буферов. Вертикальными полосами отмечены интервалы значений / на которых отношение \ш//5] имеет постоянное значение, а множитель / линейно растет с увеличением /, что и объясняет присутствие интервалов локальной монотонности. Пилообразный характер зависимости времени выполнения от размера фрагмента отчетливо прослеживается на практике (рис. 9). На вид «зубьев» влияет производительность контроллеров памяти, канала межпроцессорного соединения и параметры кеш-памяти.
t(m,s), мкс;
J7 550 ]7 500 17 450 17 400 П 350 17 300 \1 250 17 200
О Г-1 -rf S© ОС-ОС г- О m Tf rf 4D 00 о см
Tf so ос- о
Tf СП (Ч • О О 't ^ оо О N ^
го о оо vo m
- N (N (Л Tf
S S 2
Размер / буфера, байт
Рис. 8. Зависимость времени Т(т, 5) выполнения алгоритма от размера/буфера (параметры модели: т = 16 МВ, 5 = 32, Ь = /5, д = 1, Т = 1Е-9, ^ = 50т, tws = 100?, Та = 100
'N 3920
У
а 3880
« 3840
и а 3800
И 3760
,3220
^^immNNNHHOOQ^ON (Ncn^-U-jVOr-OOONQ'-iCSCO^^t-U-i
т-Н ^H i-Ч
Размер буфера (байт)
■^■■^■■^■mmcscsca^H^HOOO^os
Размер буфера (байт)
Рис. 9. Зависимость времени MPI_Bcast от размера f буфера (p = 2 процесса на одном NUMA-узле, m = 16 MB, s = 32, q = 1): а) сервер Intel Xeon Nehalem; б) сервер Intel Xeon Broadwell
5.5. Структура дерева уведомлений
От структуры дерева уведомлений зависит время tw получения процессами сигнала о готовности очередного буфера в очереди корневого процесса. Например, при использовании алгоритма плоского дерева (flat tree) листовой процесс с номером p - 1 получает каждое уведомление (W) последним и, как следствие, последним сообщает корневому процессу об освобождении буферов очереди (Л). При использовании плоского (flat tree) и линейного деревьев (chain, linear tree) время ожидания tw и two линейно O(p) зависит от числа p процессов, fc-арные и fc-номиальные деревья приводят к логарифмической зависимости времени ожидания O(logp). Наиболее распространённые на практике многопроцессорные узлы с общей памятью редко имеют более 64 процессорных ядер (Top500, 2019), для таких систем структура дерева может не оказывать определяющего решения. В OpenMPI coll/sm по умолчанию используется завершенное бинарное дерево степени 4, в MVAPICH - плоское дерево, в работе [10] fc-арные и fc-номиальные деревья не показали превосходства над плоским деревом.
На рис. 10 показана зависимость времени выполнения разработанного алгоритма от размера сообщения для различных деревьев. Наименьшее время выполнения MPI_Bcast достигнуто при использовании бинарного и линейного деревьев.
2-nomial tree
3-nomial tree
4-nomial tree
flat tree chain
2-ary tree
3-ary tree
4-ary tree
Размер сообщения (байт)
Рис. 10. Зависимость времени выполнения алгоритма MPI Bcast от размера m сообщения для деревьев различных видов: время нормализовано относительно времени для плоского дерева (flat tree); s = 64, f = 8192, q = 1, p = 16, два NUMA-узла сервера Intel Xeon Broadwell
5.6 Влияние топологии NUMA-системы
Основное время выполнения алгоритма составляет копирование корневым процессом фрагментов из входного буфера операции MPI_Bcast в свою очередь. По этой причине важно, чтобы буфер пользователя и очередь корневого процесса находились в памяти одного NUMA-узла. Без явного выделения страниц памяти с NUMA-узла текущего процесса буферы очереди могут быть размещены на NUMA-узле процесса 0, который осуществляет начальное формирование сегмента разделяемой памяти и неявно запускает механизм опережающего выделения страниц памяти с текущего NUMA-узла (linux readahead, page cache). В предложенном авторами алгоритме для установления корректной привязки страниц памяти к NUMA-узлам на время инициализации сегмента вызовом madvise (MADV_RANDOM) отключается опережающее чтение страниц. На рис. 11 показано время выполнения MPI_Bcast в режиме «опережающего чтения страниц» (readahead on) и без него (readahead off, mad-vise (MADV_RANDOM) ). Для оценки временных потерь из-за доступа к памяти удаленного NUMA-узла тест 1MB запускался с параметром "-root_shift on" - корень операции MPI_Bcast циклически изменяется от 0 до p - 1 на каждой итерации цикла измерений для заданного размера сообщения. На рис. 11 показано время выполнения алгоритма 16 процессами на двух NUMA-узлах сервера Intel Xeon Broadwell. С ростом размера сообщения увеличивается время доступа к очереди в памяти удаленного NUMA-узла. Аналогично алгоритм операции MPI_Bcast пакета MVAPICH выделяет память для очередей без учета размещения процессов по NUMA-узлам, а алгоритм OMPI coll/sm реализует частичную привязку буферов к NUMA-узлам процессов, но из-за опережающего чтения страниц (readahead) значительная их часть всегда выделяется с NUMA-узла процесса 0. На рис. 12, 13 показано время работы предложенного авторами алгоритма и алгоритмов MVAPICH и Open MPI coll/sm. За счет учета NUMA-топологии узла и распределения процессов по ним разработанный алгоритм позволил на 20-40 % сократить время выполнения операции MPI_Bcast по сравнению с алгоритмом MVAPICH и на 20-60 % процентов по сравнению с алгоритмом OpenMPI coll/sm.
readahead on
readahead off
MVAPICH
SHMBcast
cqnqœnoœnnnnncq
QO Ю N н N »1 -es »n
N ^t M «
Размер сообщения (байт)
Рис. 11. Время выполнения алгоритма при включённом (readahead on) и выключенном опережающем чтении страниц памяти (readahead off): время нормализовано относительно времени при включённом опережающем чтении; s = 64, f = 8192, q = 1, бинарное дерево, p = 16, два NUMA-узла сервера Intel Xeon Broadwell, IMB -root shift on
ччннннтнчмчнчнч
-"-"VON^OONON m CS »Л ^н
-н CS tri
Размер сообщения (байт) Рис. 12. Время выполнения разработанного алгоритма (SHMBcast) и алгоритма MVAPICH (f = 8192, s = 128): время нормализовано относительно времени MVAPICH; s = 64, f = 8192, q = 1, бинарное дерево, p = 8, два NUMA-узла сервера Intel Xeon Nehalem, IMB -root_shift on
Рис. 13. Время выполнения разработанного алгоритма (SHMBcast) и алгоритма Open MPI coll/sm (f = 8192, s = 8): время нормализовано относительно времени coll/sm; s = 64, f = 8192, q = 1, бинарное дерево, p = 16, два NUMA-узла сервера Intel Xeon Broadwell, IMB -root_shift on
6. Заключение
Разработанный алгоритм ориентирован на оптимизацию выполнения операции MPI_Bcast на многопроцессорных вычислительных NUMA-узлах. В рамках модели найдены оптимальные параметры очередей для конвейерной передачи фрагментов сообщения из корневого процесса остальным. Полученные результаты хорошо согласуются с практикой -рекомендуемые значения обеспечивают близкие к оптимальным значения размера буфера очереди и её длины. Предложенный в алгоритме метод размещения очередей процессов в памяти локальных NUMA-узлов обеспечивает меньшее на 20-60 % время выполнения операции по сравнению с MVAPICH и Open MPI coll/sm.
В дальнейшем планируется развить методы эффективной передачи сообщений, не превышающих размера f буфера очереди. Дополнительное внимание будет уделено исследованию структур деревьев уведомлений процессов, оптимизации синхронизации процессов и передачи сообщений производных типов данных (MPI derived data types). Для сообщений больших размеров целесообразным видится рассмотрение возможности использования больших страниц памяти (huge pages).
Литература
1. Thakur R., Rabenseifner R., Gropp W. Optimization of Collective Communication Operations in MPICH // High Performance Computing Applications. 2005. V. 19 (1). P. 49-66.
2. Sanders P., Speck J., Traff J. L. Two-Tree Algorithms for Full Bandwidth Broadcast, Reduction and Scan // Parallel Computing. 2009. V. 35 (12). P. 581-594.
3. Traff J., Ripke A. Optimal Broadcast for Fully Connected Processor-node Networks // Parallel and Distributed Computing. 2008. V. 68 (7). P. 887-901.
4. Bin Jia. Process Cooperation in Multiple Message Broadcast // Parallel Computing. 2009 V. 35. P. 572-580.
5. Lameter C. NUMA (Non-Uniform Memory Access): An Overview // ACM Queue. 2013. V. 11 (7). P. 1-12.
6. Li S., Hoefler T. and Snir M. NUMA-Aware Shared Memory Collective Communication for MPI // Proc. of the 22nd Int. symposium on High-performance parallel and Distributed computing, 2013. P. 85-96.
7. Wu M., Kendall R. and Aluru S. Exploring Collective Communications on a Cluster of SMPs // Proc. of the HPCAsia, 2004. P. 114-117.
8. MVAPICH: MPI over InfiniBand, Omni-Path, Ethernet/iWARP, and RoCE // URL: http://mvapich.cse.ohio-state.edu/ (дата обращения: 12.12.2019).
9. Graham R. L., Shipman G. MPI Support for Multi-core Architectures: Optimized Shared Memory Collectives // Proc. of the 15th European PVM/MPI Users' Group Meeting, 2008. P. 130-140.
10. Jain S., Kaleem R., Balmana M., Langer A., Durnov D., Sannikov A. and Garzaran M. Framework for Scalable Intra-Node Collective Operations using Shared Memory // Proc. of the International Conference for High Performance Computing, Networking, Storage, and Analysis (SC-2018), 2018. P. 374-385.
Статья поступила в редакцию 23.12.2019; переработанный вариант - 09.01.2020.
Курносов Михаил Георгиевич
д.т.н., доцент, профессор кафедры вычислительных систем Сибирского государственного университета телекоммуникаций и информатики (630102, Новосибирск, ул. Кирова, 86), тел. (383) 2-698-382, e-mail: mkurnosov@gmail. com.
Токмашева Елизавета Ивановна
аспирант, преподаватель кафедры вычислительных систем Сибирского государственного университета телекоммуникаций и информатики (630102, Новосибирск, ул. Кирова, 86), тел. (383) 2-698-275, e-mail: eliz_tokmasheva@sibguti.ru.
Shared memory based MPI broadcast algorithm for NUMA systems M. Kurnosov, E. Tokmasheva
Broadcast collective communication operation is used by many scientific applications and tend to limit overall parallel application scalability. As the number of cores per computer node keeps increasing, it becomes important for MPI to leverage shared memory for intranode communication. This paper investigates the design and optimization of Bcast operation for SMP/NUMA nodes. We describe an algorithm for Bcast that takes advantage of NUMA-specific placement of queues in memory for message transferring. On a Xeon Nehalem and Xeon Broadwell NUMA nodes, our implementation achieves on average 20-60 % speedup over Open MPI and MVAPICH.
Keywords: Bcast, broadcast, MPI, NUMA, collective communication, parallel programming, high-performance computing.