Как сортировать строки в 100 ГБ

Учитывая жесткий диск с 120 ГБ, 100 из которых заполнены строками длиной 256 и 2 ГБ, как я могу отсортировать эти строки в Java наиболее эффективно? Сколько времени потребуется?

Ответ 1

Я в основном повторяю ответ Krystian, но уточняя:

Да, вам нужно сделать это больше или меньше, поскольку у вас мало доступной оперативной памяти. Но наивные на месте виды были бы катастрофой здесь только из-за стоимости перемещения строк вокруг.

Вместо того, чтобы на самом деле перемещать строки вокруг, просто отслеживайте, какие строки нужно поменять, а какие другие и фактически переместить их, когда-то, в конце, в их последнее место. То есть, если у вас было 1000 строк, сделайте массив из 1000 целых чисел. array [i] - это место, где строка я должна заканчиваться. Если массив [17] == 133 в конце, это означает, что строка 17 должна заканчиваться в пятне для строки 133. array [i] == я для всех i, чтобы начать. Таким образом, заменяя строки, это всего лишь вопрос об обмене двумя целями.

Тогда любой локальный алгоритм, такой как quicksort, работает очень хорошо.

На время работы, безусловно, доминирует конечное перемещение строк. Предполагая, что каждый из них движется, вы перемещаете около 100 ГБ данных в сообщениях разумного размера. Я мог бы предположить, что привод/контроллер/ОС может перемещать около 100 МБ/сек для вас. Итак, 1000 секунд или около того? 20 минут?

Но он вписывается в память? У вас есть 100 ГБ строк, каждый из которых составляет 256 байт. Сколько строк? 100 * 2 ^ 30/2 ^ 8, или около 419M строк. Вам нужно 419M ints, каждый - 4 байта, или около 1,7 ГБ. Voila, вписывается в ваш 2GB.

Ответ 2

A1. Вероятно, вы захотите реализовать некоторую форму merge-sort.

A2: Дольше, чем если бы у вас на вашем компьютере была 256 ГБ оперативной памяти.

Редактировать: ужалили критикой, цитирую статью Википедии о сортировке слияния:

Сортировка слияний настолько неотъемлемо последовательна, что практично запускать ее с использованием медленных ленточных накопителей в качестве устройств ввода и вывода. Это требует очень маленькая память, а требуемая память не зависит от числа элементов данных.

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

Ответ 3

Вот как я это сделаю:

Фаза 1 состоит в том, чтобы разбить 100Gb на 50 разделов 2Gb, прочитать каждый из 50 разделов в память, отсортировать с помощью quicksort и выписать. Вы хотите отсортировать разделы на верхнем конце диска.

Этап 2 состоит в том, чтобы затем объединить 50 отсортированных разделов. Это сложный бит, потому что на диске недостаточно места для хранения разделов и окончательного сортированного вывода. Итак...

  • Сделайте 50-way merge, чтобы заполнить первый 20Gb в нижнем конце диска.

  • Сдвиньте оставшиеся данные в 50 разделах вверх, чтобы сделать еще 20Gb свободного пространства смежным с концом первого 20Gb.

  • Повторите шаги 1 и 2. до завершения.

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

РЕДАКТИРОВАТЬ - @meriton предложил умный способ уменьшить копирование. Вместо того, чтобы скользить, он предлагает, чтобы разделы были отсортированы в обратном порядке и считаны назад в фазе слияния. Это позволит алгоритму освободить дисковое пространство, используемое разделами (этап 2, шаг 2), просто обрезая файлы разделов.

Потенциальными недостатками этого являются увеличение фрагментации диска и снижение производительности за счет чтения разделов в обратном направлении. (В последнем случае чтение файла в обратном порядке в Linux/UNIX требует большего количества системных вызовов, и реализация FS может оказаться невозможной для "чтения вперед" в обратном направлении.)

Наконец, я хотел бы указать, что любые теоретические предсказания времени, проведенного этим алгоритмом (и другими), в основном являются догадками. Поведение этих алгоритмов на реальных реальных виртуальных дисках ОС + + просто слишком сложно для расчетов "назад для огибающей", чтобы дать надежные ответы. Для надлежащего лечения потребуется фактическая реализация, настройка и бенчмаркинг.

Ответ 4

Звучит как задача, которая вызывает метод External sorting. Том 3 "Искусство компьютерного программирования" содержит раздел с подробным обсуждением внешних методов сортировки.

Ответ 5

Я думаю, вы должны использовать BogoSort. Возможно, вам придется немного изменить алгоритм, чтобы разрешить сортировку inplace, но это не должно быть слишком сложно.:)

Ответ 6

Вы должны использовать trie (aka: префиксное дерево): построить древовидную структуру, которая позволяет вам легко пройдите по строкам упорядоченным образом, сравнивая их префиксы. На самом деле вам не нужно хранить его в памяти. Вы можете построить trie как дерево каталогов в вашей файловой системе (очевидно, не тот, из которого поступают данные).

Ответ 7

AFAIK, merge-sort требует столько свободного места, как у вас есть данные. Это может быть требованием для любого внешнего сорта, который позволяет избежать произвольного доступа, хотя я не уверен в этом.