В информатике оператор редукции [1] — это тип оператора , который обычно используется в параллельном программировании для редукции элементов массива в один результат. Операторы редукции ассоциативны и часто (но не обязательно) коммутативны . [2] [3] [4] Редукция наборов элементов является неотъемлемой частью моделей программирования, таких как MapReduce , где оператор редукции применяется ( отображается ) ко всем элементам до того, как они будут редуцированы. Другие параллельные алгоритмы используют операторы редукции в качестве основных операций для решения более сложных задач. Многие операторы редукции могут использоваться для широковещательной рассылки для распределения данных по всем процессорам.
Оператор сокращения может помочь разбить задачу на различные частичные задачи, вычисляя частичные результаты, которые можно использовать для получения окончательного результата. Он позволяет выполнять некоторые последовательные операции параллельно и сокращать количество шагов, необходимых для этих операций. Оператор сокращения сохраняет результат частичных задач в закрытой копии переменной. Затем эти закрытые копии объединяются в общую копию в конце.
Оператор является оператором редукции, если:
Эти два требования выполняются для коммутативных и ассоциативных операторов, применяемых ко всем элементам массива.
Некоторые операторы, удовлетворяющие этим требованиям, — это сложение, умножение и некоторые логические операторы (и, или и т. д.).
Оператор редукции может быть применен за постоянное время к входному набору векторов с элементами каждый. Результатом операции является комбинация элементов , и она должна быть сохранена на указанном корневом процессоре в конце выполнения. Если результат должен быть доступен на каждом процессоре после завершения вычисления, его часто называют Allreduce. Оптимальный последовательный алгоритм линейного времени для редукции может применять оператор последовательно от начала к концу, всегда заменяя два вектора результатом операции, примененной ко всем его элементам, тем самым создавая экземпляр, в котором на один вектор меньше. Ему нужны шаги, пока не останется только . Последовательные алгоритмы не могут работать лучше, чем за линейное время, но параллельные алгоритмы оставляют некоторое пространство для оптимизации.
Предположим, у нас есть массив . Сумма этого массива может быть вычислена последовательно путем последовательного сведения массива к одной сумме с помощью оператора '+'. Начав суммирование с начала массива, получим: Поскольку '+' является как коммутативным, так и ассоциативным, это оператор сокращения. Следовательно, это сокращение может быть выполнено параллельно с использованием нескольких ядер, где каждое ядро вычисляет сумму подмножества массива, а оператор сокращения объединяет результаты. Использование сокращения двоичного дерева позволило бы 4 ядрам вычислить , , , и . Затем два ядра могут вычислить и , и, наконец, одно ядро вычисляет . Таким образом, всего 4 ядра могут быть использованы для вычисления суммы по шагам вместо шагов, требуемых для последовательной версии. Эта параллельная техника двоичного дерева вычисляет . Конечно, результат тот же, но только из-за ассоциативности оператора сокращения. Коммутативность оператора редукции была бы важна, если бы было главное ядро, распределяющее работу по нескольким процессорам, поскольку тогда результаты могли бы возвращаться на главный процессор в любом порядке. Свойство коммутативности гарантирует, что результат будет тем же самым.
IEEE 754-2019 определяет 4 вида редукций сумм и 3 вида редукций масштабированных произведений. Поскольку операции являются операторами редукции, стандарты определяют, что «реализации могут ассоциироваться в любом порядке или оцениваться в любом более широком формате». [5]
Умножение матриц не является оператором редукции, поскольку операция не является коммутативной. Если бы процессам было разрешено возвращать результаты умножения матриц в любом порядке главному процессу, конечный результат, который вычисляет главный процесс, вероятно, будет неверным, если результаты поступили не в том порядке. Однако следует отметить, что умножение матриц ассоциативно, и поэтому результат будет правильным, если соблюдается правильный порядок, как в методе редукции двоичного дерева.
Что касается параллельных алгоритмов, то существуют две основные модели параллельных вычислений: параллельная машина с произвольным доступом (PRAM) как расширение RAM с общей памятью между процессорами и объемный синхронный параллельный компьютер , который учитывает связь и синхронизацию . Обе модели имеют различные последствия для временной сложности , поэтому будут показаны два алгоритма.
Этот алгоритм представляет собой широко распространенный метод обработки входов, где — степень двойки. Обратная процедура часто используется для трансляции элементов. [6] [7] [8]
Бинарный оператор для векторов определяется поэлементно таким образом, что
Алгоритм далее предполагает, что в начале для всех и является степенью двойки и использует блоки обработки . В каждой итерации половина блоков обработки становятся неактивными и не участвуют в дальнейших вычислениях. На рисунке показана визуализация алгоритма, использующего сложение в качестве оператора. Вертикальные линии представляют блоки обработки, где происходит вычисление элементов на этой строке. Восемь входных элементов расположены внизу, и каждый шаг анимации соответствует одному параллельному шагу в выполнении алгоритма. Активный процессор оценивает заданный оператор на элементе, который он в данный момент удерживает, и где — минимальный индекс, выполняющий , так что становится неактивным процессором на текущем шаге. и не обязательно являются элементами входного набора , поскольку поля перезаписываются и повторно используются для ранее оцененных выражений. Для координации ролей блоков обработки на каждом шаге без возникновения дополнительной связи между ними используется тот факт, что блоки обработки индексируются номерами от до . Каждый процессор смотрит на свой -й наименее значимый бит и решает, следует ли ему стать неактивным или вычислить оператор на своем собственном элементе и элементе с индексом, где -й бит не установлен. Базовая схема связи алгоритма - это биномиальное дерево, отсюда и название алгоритма.
Только сохраняет результат в конце, поэтому он является корневым процессором. Для операции Allreduce результат должен быть распределен, что можно сделать, добавив трансляцию из . Кроме того, количество процессоров ограничено степенью двойки. Это можно снять, увеличив количество процессоров до следующей степени двойки. Существуют также алгоритмы, которые более приспособлены для этого варианта использования. [9]
Основной цикл выполняется раз, время, необходимое для выполнения части параллельно, составляет в поскольку блок обработки либо объединяет два вектора, либо становится неактивным. Таким образом, параллельное время для PRAM составляет . Стратегия обработки конфликтов чтения и записи может быть выбрана как ограничительная, например, исключительное чтение и исключительная запись (EREW). Ускорение алгоритма составляет и, следовательно, эффективность составляет . Эффективность страдает, поскольку половина активных блоков обработки становится неактивной после каждого шага, поэтому блоки активны на шаге .
В отличие от алгоритма PRAM, в модели распределенной памяти память не делится между процессорными блоками, и данные должны явно обмениваться между процессорными блоками. Поэтому данные должны явно обмениваться между блоками, как можно увидеть в следующем алгоритме.
Единственное отличие распределенного алгоритма от версии PRAM — включение явных примитивов связи, принцип работы остается прежним.
Связь между блоками приводит к некоторым накладным расходам. Простой анализ алгоритма использует BSP-модель и включает время, необходимое для инициирования связи, и время, необходимое для отправки байта. Тогда результирующее время выполнения равно , так как элементы вектора отправляются в каждой итерации и имеют общий размер.
Для моделей распределенной памяти может иметь смысл использовать конвейерную связь. Это особенно актуально, когда мало по сравнению с . Обычно линейные конвейеры разбивают данные или задачи на более мелкие части и обрабатывают их поэтапно. В отличие от алгоритмов биномиального дерева, конвейерный алгоритм использует тот факт, что векторы не являются неразделимыми, но оператор может быть оценен для отдельных элементов: [10]
Важно отметить, что операции отправки и получения должны выполняться одновременно, чтобы алгоритм работал. Результирующий вектор сохраняется в конце. Сопутствующая анимация показывает выполнение алгоритма на векторах размером четыре с пятью процессорными блоками. Два шага анимации визуализируют один шаг параллельного выполнения.
Число шагов в параллельном выполнении равно , шаги требуются до тех пор, пока последний процессорный блок не получит свой первый элемент, и дополнительные , пока не будут получены все элементы. Таким образом, время выполнения в BSP-модели равно , предполагая, что — общий размер вектора в байтах.
Хотя имеет фиксированное значение, можно логически сгруппировать элементы вектора вместе и уменьшить . Например, пример проблемы с векторами размером четыре можно обработать, разделив векторы на первые два и последние два элемента, которые всегда передаются и вычисляются вместе. В этом случае на каждом шаге отправляется удвоенный объем, но количество шагов примерно уменьшается вдвое. Это означает, что параметр уменьшается вдвое, в то время как общий размер байта остается прежним. Время выполнения для этого подхода зависит от значения , которое можно оптимизировать, если известны и . Он оптимален для , предполагая, что это приводит к меньшему , которое делит исходный.
Редукция — одна из основных коллективных операций, реализованных в интерфейсе передачи сообщений , где производительность используемого алгоритма важна и постоянно оценивается для различных вариантов использования. [11]
Операторы могут использоваться в качестве параметров для MPI_Reduce
и MPI_Allreduce
, с той разницей, что результат доступен на одном (корневом) процессоре или на всех из них.
OpenMP предлагает редукционный пункт для описания того, как результаты параллельных операций собираются вместе. [12]
MapReduce в значительной степени опирается на эффективные алгоритмы сокращения для обработки больших наборов данных, даже в огромных кластерах. [13] [14]
Некоторые параллельные алгоритмы сортировки используют сокращения, чтобы иметь возможность обрабатывать очень большие наборы данных. [15]