Резюме. Мы обсуждаем здесь наилучшие способы оптимизации сортировки больших массивов составных объектов по нечисловым ключам. Также рассматривается способ уменьшения количества выполняемых операций (сложность) имеющихся алгоритмов сортировки. Конкретный базовый алгоритм сортировки выбирается разработчиком по своему усмотрению (см. условие 1 в замечаниях).
Введение: Проблематикой имплементации паралелльной сортировки занимаются разные специалисты по всему миру. Подавляющее число специалистов рассматривает платформы GPU и их суперскалярность как эффективную (в вычислительном плане) базу для разработки новых алгоритмов. На Хабре исследованиями в этой области занимались [KS1], [KILY1], [PatZ1], [Ms1].
Стоит отметить несколько минусов выбранного авторами похода:
объем обрабатываемого массива жестко ограничен возможностями оборудования
в исследованиях уделяется вопрос сортировки исключительно простых типов данных: натуральных чисел; вопрос сортировки более сложных и составных типов не рассматривается
при сортировке на GPU отсутствует возможность эффективной работы с указателями; как следствие сложно обеспечить сортировку составных объектов, по не числовым/составным ключам, особенно, когда сортируемые объекты хранятся во внешней памяти
проблема останова (трудно в концепции SIMD сформировать условие завершения работы алгоритма)
Автор полагает, что приведенные недостатки при сортировке на GPU и подобных суперскалярных платформах (использование концепции SIMD вообще), являются фундаментально неустранимыми.
Проведенные автором методики полностью лишены указанных недостатков, более ̶ позволяют разработчику выбирать базовый алгоритм сортировки в соответствии с требованиями к результату (устойчивость, память, ..), однако требует соблюдения следующих условий:
Условие 1: К описанной далее оптимизации склонны только алгоритмы со сложностью в худшем и среднем случаях O(NlogN)
Условие 2: Сортируемые объекты должны быть способны к группировке (кластеризации) по своим ключам[1]
Условие 3: Объекты одного класса (элементы одной группы), должны быть строго меньше/больше объектов других классов.
[1] Например сортируемыми объектами могут выступать записи вида {name: 'latin_string', age: number, born: date}
, хранимые на жестком диске. Здесь сортируемый ключ: поле 'name' объектов - английская строка неопределенной длины
Условие3 позволяет упорядочивать группы объектов между собой.
Как показали исследования условиям 2 и 3 соответствуют не только объекты с числовыми ключами, но и со строковыми (текстовыми), логическими и вещественными ключами (см. ниже "кластеризация объектов с не числовыми ключами")
Рассмотрим алогоритм сортировки со сложностью и массив из N объектов (удовлетворяющих условиям 2 и 3). Выполним кластеризацию элементов и разобьем массив на k>0 групп (предельное значение k определяется природой сортируемых объектов). Отсортируем каждую группу объектов независимо от других. При этом (учитывая условие 1) мы потратим в среднем
операций.А на независимую сортировку всех k групп мы потратим в среднем
. Преобразуя это выражение получим:
(2)
Из (2) видно, что суммарное число потраченных на сортировку операций снижается. Как не трудно заметить чем выше k (больше групп в исходном массиве), тем меньше итоговая (конечная) сложность сортировки. В купе с возможностью сортировки каждой группы элементов массива независимо, такой подход открывает путь для построения высокоэффективных алгоритмов сортировки без строгих ограничений на объем и природу сортируемых данных.
Сортировка массива таким способом требует предварительной обработки: кластеризации элементов. Наиболее эффективным алгоритмом кластеризации со сложностью O(n) является поразрядная сортировка (называемая другими авторами "Американский флаг" [VM2]). Данный класс алгоритмов способен к потоковой обработке массива в один проход, а также допускает паралеллизацию (с неизбежным интенсивным обменом между процессами по кластеризации). В качестве дополнительного бонуса предварительной обработки сортируемого массива является возможность проведения аналитики массива. Возможно массив вообще не требует сортировки или элементы одного класса (группы) уже упорядочены и данная группа может быть исключена из обработки.
кластеризация объектов с нечисловыми ключами. Кластеризация объектов, ключи которых представляют собой текстовые строки, может производиться по первым буквам строк, порождая тем самым 26 групп. Возможно снизить количество групп, объединяя уже сами буквы в группы. Например, все объекты с ключами, начинающимися с букв a-f относят к группе 0, начинающимися с букв g-l относят к группе 1, и так далее.
Кластеризация объектов с ключами в виде вещественных чисел легко производится по диапазонам значений (предварительно необходимо уточнить реальные границы ключей: мин.макс).
Измерения. Автором был проверен предложенный подход на практике, с получением следующих результатов (табл.1). Сбор статистики осуществлял специальный прокси-массив, подсчитывающий реальное число обменов, сравнений и обращений к ключам сортируемых объектов. Сортировка производилась при помощи алгоритмов "Плавная сортировка" и "Сортировка восходящей кучей".В полученных данных учтены также операции, выполненные на этапах кластеризации массива.
Таблица 1. Подсчет выполненных операций над массивами при разных техниках сортировки:
N/k |
обменов |
сравнений |
доступов к указателю |
перезаписей указателя |
обращений к ключу |
64/1 |
356 |
390 |
611 |
452 |
908 |
64/16 |
232 |
260 |
356 |
264 |
520 |
512/1 |
4319 |
4683 |
6366 |
5087 |
10390 |
512/16 |
3422 |
3691 |
4442 |
3678 |
7382 |
10k/1 |
126855 |
135333 |
166854 |
141855 |
290666 |
10k/16 |
110872 |
118006 |
130868 |
115872 |
236012 |
1M/16 |
19283612 |
20130313 |
23283611 |
20783612 |
42260626 |
17668264 |
18434186 |
19668260 |
18168264 |
36868372 |
первая строка таблицы - затраты на сортировку массива из 64 элементов целиком
вторая строка таблицы - затраты на сортировку того же массива, после разделения на 16 частей
третья строка - затраты на сортировку массива 512 элементов
четвертая строка - затраты на сортировку того же массива после разделения на 16 частей
последние две строки таблицы - данные о сортировке массива из 1 млн. элементов (стоит обратить внимание, что сортировка такого массива 16ю независимыми частями сокращает на 14 млн. число выполняемых операций даже с учетом операций по предварительной кластеризации поразрядной сортировкой)
Ссылки на другие работы по теме:
[KS1] Параллельная сортировка методом пузырька на CUDA
[KILY1] Сортировка массивов фиксированной длины с применением SIMD
[PatZ1] Параллельная сортировка данных в GPU
[Ms1] Параллельный метод сортировки массива std::thread
[VM2] Сортировка «Американский флаг»
Комментарии (18)
Dhwtj
16.02.2025 01:11N(logN - log k)=NlogN - Nlogk
То есть даже теоретически с учётом размеров величин выигрыш мизерный
KaminskyIlya Автор
16.02.2025 01:11Я правильно понял что экономия 16 млн операций при сортировке 1млн объектов, для вас мизерная?
Это не считая возможности без проблем выполнить сортировку параллельно в 16 потоков.
Dhwtj
16.02.2025 01:11N = миллиарды
K = тысячи
Посчитайте
KaminskyIlya Автор
16.02.2025 01:11Хорошо. Пусть N=10^9, k=1000,
Тогда Nlogk=10^9*10(примерно 10 - логарифма тут двоичный).
Итого почти 10 млд. Операций экономия. Среди этих операций-операции сравнения ключей, которые для составных ключей могут быть очень не быстрыми. Стоимость сравнения строк, например, O(n). Так что насчет мизерной выгоды нисколько не согласен.
dmitry_rozhkov
16.02.2025 01:11Но если все кластеры сортировать параллельно, то по времени выигрыш должен получиться больше. Нет?
Kealon
16.02.2025 01:11Кластеризация сама по себе понижает затраты и без параллельности. Проблема только в том, что она не всегда возможна.
Классически, можно поделить и петлями quick sort.
Плавная сортировка в худшем случае O(N (log(N)^2))
KaminskyIlya Автор
16.02.2025 01:11Интересное мнение. А можете провести примеры когда кластеризация невозможна?
Сложность плавной сортировки брал на Википедии. Об оценке, что вы привели - не слышал.
Спасибо за конструктивный комментарий
KaminskyIlya Автор
Стоит заметить, что автор испугался далее повышать размер сортируемого массива (хотя на млрд. элементов в тестах замахивался), боясь получить отрицательные значения выгоды, и ненароком разорвать структуру пространства-бытия.
dyadyaSerezha
Да, речь же шла о супербольших объёмах, а тут все закончилось несчастным миллионом. Странно.
Как дилетант, интересуюсь - а нельзя ли сортировать такие массивы на каждом из сотен (тысяч?) маленьких процессоров GPU параллельно? Или так и делается уже?
KaminskyIlya Автор
Представьте общественности алгоритм сортировки на GPU составных объектов, физически размещаемых на файловых носителях. большинство авторов больше как сортировку натуральных чисел предложить не могут. даже сортировку строк никто не предложил. А все потому что при сортировке подобных данных вас необходимо работать с указателями. Т.е. вы фактически перемещаете указатели, а сами данные физически не сортируются. В этом проблема
KaminskyIlya Автор
Прошу меня простить за столь резкий комментарий. Вы не обязаны ничего представлять общественности, наоборот это я должен доказывать. Но проблемы сортировки на GPU что я озвучил выше - никуда не делись. И я действительно не встречал годные реализации сортировок на GPU для, например, использования СУБД
unreal_undead2
Гугл сходу такое выдаёт - в реальной жизни не работает?
dyadyaSerezha
Ничего не понял. В чем проблема перемещать сами объекты в файлах? Как напишешь, так и будет.
KaminskyIlya Автор
Да тут один человек сказал, что выигрыш мизерный . Даже если бы я представил измерения для 100ГБ элементов - вряд ли это бы повлияло на столь высокую оценку.
На основе представленных измерений тенденция видна. Как доказательство, что концепция верна .
KaminskyIlya Автор
Я специально для вас опубликую код и замеры на массиве из 1 миллиарда записей. Но чуть позже. Просто мне необходимо привести свой экспериментальный проект в божеский вид. А то в процессе экспериментов там лапша по-гуще ассамблера сейчас
Такой размер подойдёт? Сортируемые данные будут находить на диске в момент выполнения сортировки. В оперативную память моего старенького компьютера они не поместится. Поэтому сортироваться будут по факту индексы( указатели на json-записи).Сортируемые данные будут являться json-записями , ключом сортировки - будет текстовое поле объекта длиной до 16 символов.
Сделаем замеры скорости в операциях и по времени. Вас устроит такой вариант теста?
dyadyaSerezha
16 байт? Не катит. Катит до 2К, причём лексически,причем с учётом locale.
И про индексы не понял. То надо перемещать данные, то не надо...