Я решаю проблему, и она включает в себя сортировку 10 чисел (int32) очень быстро. Мое приложение должно сортировать 10 чисел в миллионы раз как можно быстрее. Я выбираю набор данных из миллиардов элементов, и каждый раз мне нужно выбрать из него 10 чисел (упрощенно) и отсортировать их (и сделать выводы из отсортированного списка из 10 элементов).
В настоящее время я использую сортировку вставками, но я представляю, что мог бы реализовать очень быстрый собственный алгоритм сортировки для моей конкретной задачи из 10 чисел, который превзошел бы сортировку вставками.
У кого-нибудь есть идеи о том, как подойти к этой проблеме?
if
операторов должен работать лучше всего. Избегайте петель.Ответы:
(Вслед за предложением HelloWorld изучить сети сортировки.)
Кажется, что сеть из 29 сравнений / свопов - самый быстрый способ сделать сортировку с 10 входами. Я использовал сеть, обнаруженную Ваксманом в 1969 году для этого примера в Javascript, который должен быть переведен непосредственно в C, поскольку это просто список
if
утверждений, сравнений и свопов.Вот графическое представление сети, разделенной на независимые фазы. Чтобы воспользоваться преимуществами параллельной обработки, группировка 5-4-3-4-4-4-3-2 может быть преобразована в группировку 4-4-4-4-4-4-3-2.
источник
#define SORTPAIR(data, i1, i2) if (data[i1] > data[i2]) { int swap = data[i1]... }
Когда вы имеете дело с этим фиксированным размером, взгляните на Сортировка сетей . Эти алгоритмы имеют фиксированное время выполнения и не зависят от их ввода. Для вашего варианта использования у вас нет таких накладных расходов, как у некоторых алгоритмов сортировки.
Битонная сортировка - это реализация такой сети. Этот лучше всего работает с len (n) <= 32 на процессоре. На больших входах вы можете подумать о переходе на графический процессор. https://en.wikipedia.org/wiki/Sorting_network
Кстати, здесь есть хорошая страница для сравнения алгоритмов сортировки (хотя в ней отсутствует
bitonic sort
.http://www.sorting-algorithms.com
источник
Используйте сортировочную сеть, у которой есть сравнения в группах по 4, так что вы можете сделать это в SIMD-регистрах. Пара упакованных инструкций min / max реализует упакованную функцию компаратора. Извините, у меня нет времени сейчас искать страницу, которую я помню об этом, но, надеюсь, поиск по SIMD или SSE сортировочным сетям что-то даст.
x86 SSE имеет упакованные 32-битные целочисленные инструкции min и max для векторов четырех 32-битных целых. AVX2 (Haswell и более поздние версии) имеют то же самое, но для векторов 256b по 8 дюймов. Есть также эффективные инструкции перемешивания.
Если у вас много независимых небольших сортировок, может быть возможно выполнить 4 или 8 сортировок параллельно, используя векторы. Особенно если вы выбираете элементы случайным образом (так что сортируемые данные в любом случае не будут непрерывными в памяти), вы можете избежать перемешивания и просто сравнивать их в нужном вам порядке. 10 регистров для хранения всех данных из 4 (AVX2: 8) списков по 10 дюймов по-прежнему оставляют 6 регистров на пустом месте.
Сети векторной сортировки менее эффективны, если вам также необходимо отсортировать связанные данные. В этом случае наиболее эффективным способом, по-видимому, является использование упакованного сравнения для получения маски, элементы которой были изменены, и использование этой маски для смешивания векторов (ссылок на) связанных данных.
источник
Как насчет развернутой сортировки без веток?
http://coliru.stacked-crooked.com/a/71e18bc4f7fa18c6
Единственные соответствующие строки - первые два
#define
.Он использует два списка и полностью перепроверяет первый из них десять раз, что было бы плохо реализованной сортировкой выбора, однако он избегает ветвей и циклов переменной длины, которые могут компенсировать это современными процессорами и таким небольшим набором данных.
эталонный тест
Я сравнил сеть сортировки, и мой код выглядит медленнее. Однако я попытался удалить развертывание и копию. Запуск этого кода:
Я последовательно получаю лучший результат для сортировки без ветвления по сравнению с сетью сортировки.
источник
for ( ; i<10; i++) (m > a[i]) && (m = a[i], indx = i );
не очень хорошо оптимизирована. (короткое замыкание обычно является формой ветвления)std::shuffle
сfor (int n = 0; n<10; n++) a[n]=g();
. Время выполнения уменьшилось вдвое, и теперь сеть работает быстрее.std::sort
?std::sort
но он работал так плохо, что даже не включил его в тест. Я предполагаю, что с крошечными наборами данных это довольно накладные расходы.Вопрос не говорит о том, что это какое-то веб-приложение. Единственное, что бросилось в глаза, было:
Как инженер по программному и аппаратному обеспечению это абсолютно кричит "FPGA" для меня. Я не знаю, какие выводы вы должны сделать из отсортированного набора чисел или откуда поступают данные, но я знаю, что было бы почти тривиально обрабатывать где-то от ста миллионов до миллиарда этих «сортировать и анализировать "операций в секунду . В прошлом я занимался секвенированием ДНК с помощью FPGA. Почти невозможно превзойти огромную вычислительную мощность ПЛИС, когда проблема хорошо подходит для решения такого типа.
На каком-то уровне единственным ограничивающим фактором становится то, как быстро вы можете поместить данные в FPGA и как быстро вы можете получить их.
Для справки я разработал высокопроизводительный процессор обработки изображений в реальном времени, который получал 32-битные данные RGB-изображения со скоростью около 300 миллионов пикселей в секунду. Данные передаются на другой конец через фильтры КИХ, умножители матриц, таблицы поиска, блоки обнаружения краев пространства и ряд других операций. Все это на сравнительно небольшой FPGA Xilinx Virtex2 с тактовой частотой от 33 МГц до, если я правильно помню, 400 МГц. О, да, он также имел реализацию контроллера DDR2 и управлял двумя банками памяти DDR2.
ПЛИС может выводить своего рода десять 32-битных чисел на каждом тактовом переходе, работая на сотнях МГц. В начале операции будет небольшая задержка, поскольку данные заполняют конвейер обработки данных. После этого вы сможете получить один результат за часы. Или больше, если обработка может быть распараллелена посредством репликации конвейера сортировки и анализа. Решение в принципе почти тривиально.
Дело в том, что если приложение не привязано к ПК, а поток данных и обработка «совместимы» с решением FPGA (автономным или в виде карты сопроцессора в машине), вы не сможете иметь возможность превзойти достижимый уровень производительности с помощью программного обеспечения, написанного на любом языке, независимо от алгоритма.
РЕДАКТИРОВАТЬ:
Просто запустил быстрый поиск и нашел бумагу, которая может быть вам полезна. Похоже, это восходит к 2012 году. Вы можете сделать намного лучше в производительности сегодня (и даже тогда). Вот:
Сортировка сетей на ПЛИС
источник
Недавно я написал небольшой класс, который использует алгоритм Бозе-Нельсона для генерации сети сортировки во время компиляции.
С его помощью можно создать очень быструю сортировку по 10 числам.
Обратите внимание, что вместо
if (compare) swap
утверждения мы явно кодируем троичные операторы для min и max. Это поможет подтолкнуть компилятор к использованию кода без ветвей.Ориентиры
Следующие тесты скомпилированы с помощью clang -O3 и запущены на моем MacBook Air в середине 2012 года.
Сортировка случайных данных
Сравнивая его с кодом DarioP, вот количество миллисекунд, потраченных на сортировку 1 миллиона 32-битных массивов int размером 10:
Сортировка в жестком коде Net 10: закодированная 88,774 мс Шаблонная сортировка
Бозе-Нельсона 10: 27,815 мс
Используя этот шаблонный подход, мы также можем генерировать сети сортировки по времени компиляции для другого числа элементов.
Время (в миллисекундах) сортировать 1 миллион массивов разных размеров.
Количество миллисекунд для массивов размером 2, 4, 8 составляет 1,943, 8,655 и 20,246 соответственно.
Кредиты для Гленну Тейтельбауму для развернутого рода вставок.
Вот средние часы на сортировку для маленьких массивов из 6 элементов. Код теста и примеры можно найти по этому вопросу:
Самый быстрый вид массива с фиксированной длиной 6 int
Он работает так же быстро, как самый быстрый пример в вопросе для 6 элементов.
Производительность по сортировке отсортированных данных
Часто входные массивы могут быть уже отсортированы или в основном отсортированы.
В таких случаях вставка сортировки может быть лучшим выбором.
Вы можете выбрать подходящий алгоритм сортировки в зависимости от данных.
Код, используемый для тестов, можно найти здесь .
источник
v1 = v0 < v1 ? v1 : v0; // Max
может все еще отрасли, в этом случае он может быть заменен ,v1 += v0 - t
потому что , еслиt
есть ,v0
тоv1 + v0 -t == v1 + v0 - v0 == v1
ещеt
естьv1
иv1 + v0 -t == v1 + v0 - v1 == v0
maxss
илиminss
на современных компиляторах. Но в тех случаях, когда это не работает, можно использовать другие способы обмена. :)Хотя сетевая сортировка имеет хорошие шансы быть быстрой на небольших массивах, иногда вы не можете превзойти сортировку вставкой, если она должным образом оптимизирована. Например, пакетная вставка с 2 элементами:
источник
in[y+2]= in[y];
, опечатка?Вы можете полностью развернуть
insertion sort
Чтобы сделать это проще,
template
можно использовать рекурсивные s без каких-либо дополнительных функций. Поскольку он уже естьtemplate
,int
может быть иtemplate
параметром. Это также делает размеры массива кодирования, отличные от 10, тривиальными для создания.Обратите внимание, что сортировать
int x[10]
вызовыinsert_sort<int, 9>::sort(x);
так как класс использует индекс последнего элемента. Это можно обернуть, но это будет больше кода для чтения.В моем тестировании это было быстрее, чем примеры сортировки сети.
источник
По тем же причинам , которые я описал здесь , на следующие функции сортировки,
sort6_iterator()
иsort10_iterator_local()
, должен выполнять хорошо, когда сортировка сеть была взята из здесь :Для вызова этой функции я передал ей
std::vector
итератор.источник
Для сортировки вставкой требуется в среднем 29,6 сравнений для сортировки 10 входных данных с наилучшим регистром из 9 и худшим из 45 (с учетом входных данных в обратном порядке).
{9,6,1} Оболочка сортировки потребует в среднем 25,5 сравнений для сортировки 10 входных данных. Наилучший случай - 14 сравнений, наихудший - 34, а для сортировки обратного ввода требуется 22.
Таким образом, использование сортировки по типу оболочки вместо сортировки по вставке уменьшает средний случай на 14%. Хотя лучший случай увеличен на 56%, худший - на 24%, что важно в приложениях, где важно контролировать производительность наихудшего случая. Обратный случай уменьшен на 51%.
Поскольку вы, похоже, знакомы с сортировкой вставок, вы можете реализовать алгоритм как сеть сортировки для {9,6}, а затем добавить сортировку вставок ({1}) после этого:
источник