Внешняя сортировка — это класс алгоритмов сортировки , которые могут обрабатывать огромные объемы данных . Внешняя сортировка требуется, когда сортируемые данные не помещаются в основную память вычислительного устройства (обычно ОЗУ ) и вместо этого должны находиться в более медленной внешней памяти , обычно на диске . Таким образом, внешние алгоритмы сортировки являются алгоритмами внешней памяти и, следовательно, применимы в модели внешней памяти вычислений .
Внешние алгоритмы сортировки обычно делятся на два типа: сортировка распределением, которая напоминает быструю сортировку , и внешняя сортировка слиянием, которая напоминает сортировку слиянием . Внешняя сортировка слиянием обычно использует гибридную стратегию сортировки-слияния. На этапе сортировки фрагменты данных, достаточно малые для того, чтобы поместиться в основной памяти, считываются, сортируются и записываются во временный файл. На этапе слияния отсортированные подфайлы объединяются в один более крупный файл.
Внешние алгоритмы сортировки могут быть проанализированы в модели внешней памяти . В этой модели кэш или внутренняя память размером M и неограниченная внешняя память делятся на блоки размером B , а время выполнения алгоритма определяется количеством передач памяти между внутренней и внешней памятью. Как и их аналоги , не обращающие внимания на кэш , асимптотически оптимальные внешние алгоритмы сортировки достигают времени выполнения (в нотации Big O ) .
Одним из примеров внешней сортировки является внешний алгоритм сортировки слиянием , который использует алгоритм слияния K-way . Он сортирует фрагменты, каждый из которых помещается в ОЗУ, а затем объединяет отсортированные фрагменты вместе. [1] [2]
Алгоритм сначала сортирует M элементов за раз и помещает отсортированные списки обратно во внешнюю память. Он выполняет -way merge для этих отсортированных списков, рекурсивно, если основной памяти недостаточно для эффективного объединения за один проход. Во время прохода merge B элементов из каждого отсортированного списка находятся во внутренней памяти, и минимум повторно выводится.
Например, для сортировки 900 мегабайт данных с использованием всего 100 мегабайт оперативной памяти:
Проход слияния является ключом к тому, чтобы внешняя сортировка слиянием работала внешне. Алгоритм слияния делает только один проход по каждому фрагменту, поэтому фрагменты не должны загружаться все сразу; вместо этого последовательные части фрагмента загружаются по мере необходимости. И пока считываемые блоки относительно большие (например, 10 МБ в этом примере), чтения могут быть относительно эффективными даже на носителях с низкой производительностью случайного чтения, таких как жесткие диски.
Исторически вместо сортировки иногда использовался алгоритм замены-выбора [3] для выполнения начального распределения, чтобы создать в среднем вдвое меньше выходных фрагментов вдвое большей длины.
Предыдущий пример — это сортировка в два прохода: сначала сортировка, затем слияние. Сортировка заканчивается одним k -сторонним слиянием, а не серией двухсторонних проходов слияния, как в типичной сортировке слиянием в памяти. Это происходит потому, что каждый проход слияния считывает и записывает каждое значение с диска и на диск, поэтому сокращение количества проходов более чем компенсирует дополнительную стоимость k -стороннего слияния.
Ограничение на однопроходное слияние заключается в том, что по мере увеличения количества фрагментов память будет делиться на большее количество буферов, поэтому каждый буфер будет меньше. В конце концов, чтения станут настолько маленькими, что больше времени будет тратиться на поиск на диске, чем на передачу данных. Типичный магнитный жесткий диск может иметь время доступа 10 мс и скорость передачи данных 100 МБ/с, поэтому каждый поиск займет столько же времени, сколько передача 1 МБ данных.
Таким образом, для сортировки, скажем, 50 ГБ в 100 МБ ОЗУ использование одного прохода слияния на 500 путей неэффективно: мы можем прочитать только 100 МБ / 501 ≈ 200 КБ из каждого фрагмента за раз, поэтому 5/6 времени диска тратится на поиск. Использование двух проходов слияния решает проблему. Тогда процесс сортировки может выглядеть следующим образом:
Хотя это требует дополнительного прохода по данным, каждое чтение теперь имеет длину 4 МБ, поэтому только 1/5 времени диска тратится на поиск. Улучшение эффективности передачи данных во время проходов слияния (16,6% до 80% — почти 5-кратное улучшение) более чем компенсирует удвоенное количество проходов слияния.
Вариации включают использование промежуточного носителя, такого как твердотельный диск , для некоторых этапов; быстрое временное хранилище не должно быть достаточно большим, чтобы вместить весь набор данных, просто должно быть существенно больше, чем доступная основная память. Повторяя пример выше с 1 ГБ временного хранилища SSD, первый проход может объединить 10×100 МБ отсортированных фрагментов, считанных из этого временного пространства, для записи 50x1 ГБ отсортированных фрагментов на HDD. Высокая пропускная способность и пропускная способность случайного чтения SSD помогают ускорить первый проход, а чтения HDD для второго прохода могут быть 2 МБ, достаточно большими, чтобы поиск не занимал большую часть времени чтения. SSD также могут использоваться в качестве буферов чтения на этапе слияния, позволяя выполнять меньше больших чтений (в этом примере 20 МБ чтения) с хранилища HDD. Учитывая более низкую стоимость емкости SSD по сравнению с ОЗУ, SSD могут быть экономичным инструментом для сортировки больших входных данных с очень ограниченной памятью.
Как и сортировки в памяти, эффективные внешние сортировки требуют O ( n log n ) времени: экспоненциально растущие наборы данных требуют линейно увеличивающегося числа проходов, каждый из которых занимает O(n) времени. [4] При разумных предположениях не менее 500 ГБ данных, хранящихся на жестком диске, могут быть отсортированы с использованием 1 ГБ основной памяти, прежде чем третий проход станет выгодным, и во много раз больше данных можно будет отсортировать, прежде чем четвертый проход станет полезным. [5]
Размер основной памяти важен. Удвоение памяти, выделенной для сортировки, вдвое сокращает количество фрагментов и количество чтений на фрагмент, уменьшая количество требуемых поисков примерно на три четверти. Соотношение оперативной памяти к дисковому хранилищу на серверах часто делает удобным выполнение огромных сортировок на кластере машин [6], а не на одной машине с несколькими проходами. Носители с высокой производительностью случайного чтения, такие как твердотельные накопители (SSD), также увеличивают объем, который может быть отсортирован до дополнительных проходов, повышающих производительность.
Сортировка внешним распределением аналогична быстрой сортировке . Алгоритм находит приблизительно опорные точки и использует их для деления N элементов на приблизительно равные по размеру подмассивы, каждый из которых меньше следующего, а затем рекурсирует до тех пор, пока размеры подмассивов не станут меньше размера блока . Когда подмассивы меньше размера блока, сортировка может быть выполнена быстро, поскольку все чтения и записи выполняются в кэше , а во внешней модели памяти требуются операции.
Однако нахождение именно опорных точек не было бы достаточно быстрым, чтобы сделать внешнюю сортировку распределения асимптотически оптимальной . Вместо этого мы находим немного меньше опорных точек. Чтобы найти эти опорные точки, алгоритм разбивает N входных элементов на части, берет каждый элемент и рекурсивно использует алгоритм медианы медиан для поиска опорных точек. [7]
Существует двойственность или фундаментальное сходство между алгоритмами, основанными на слиянии и распределении. [8]
Sort Benchmark, созданный компьютерным ученым Джимом Греем , сравнивает внешние алгоритмы сортировки, реализованные с использованием тонко настроенного оборудования и программного обеспечения. Победившие реализации используют несколько методов: