![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Boost.SortBoost , Boost.Sort ,
|
![]() |
Warning |
---|---|
Все эти алгоритмы работают только наитераторах случайного доступа.. |
Они являются гибридами, использующими как радикс, так и сортировку на основе сравнения, специализированную для сортировки общих типов данных, таких как целые числа, поплавки и строки.
Эти алгоритмы кодируются общим способом и принимают функторы, что позволяет им сортировать любой объект, который может быть обработан, как эти основные типы данных. В случае<
, это включает в себя все с определенным строгим слабым упорядочением, чтоstd::sortможет сортировать, но написание эффективных функторов для некоторых сложных ключевых типов может не стоить дополнительных усилий по сравнению с просто использованиемstd::sort, в зависимости от того, насколько важна скорость для вашего приложения. Использование образцов доступно в каталоге примеров.string_sort
>
В отличие от многих алгоритмов, основанных на радиксе, основной алгоритм<
разработан вокругнаихудшей производительности. Он работает лучше на кусочных данных (где он не широко распространен), так что на реальных данных он может работать значительно лучше, чем на случайных данных. Концептуально<spreadsort
>
может сортировать любые данные, для которых может быть определен абсолютный порядок, и<spreadsort
>
достаточно гибок, чтобы это было возможно.string_sort
>
<
——— [] — [] — [[]:spreadsort
>
spreadsort
>
имеет оптимизацию, чтобы выйти на ранней стадии в этом случае.<
——— [] — [] — [[]:spreadsort
>
spreadsort
>
быстрее на данных обратного порядка, чем на рандомизированных данных, ноstd:::sortв этом особом случае ускоряется больше.spreadsort
>
кstd::sort, если размер ввода меньше 1000, поэтому производительность идентична для небольших объемов данных на практике.Эти функции определены в<namespace
boost::sort::spreadsort
>.
![]() |
Tip |
---|---|
В подъёме. Сортировать C++ В справочном разделе нажмите на соответствующую перегрузку, например< |
Каждый из<
,<integer_sort
>
и<float_sort
>
имеет 3 основных варианта: Базовая версия, которая берет первый итератор и последний итератор, какstd::sort:string_sort
>
integer_sort(array.begin(), array.end()); float_sort(array.begin(), array.end()); string_sort(array.begin(), array.end());
Версия с перегруженным фанктором сдвига, обеспечивающая гибкость в случае, если<operator>>
>уже делает что-то другое, кроме сдвига. Функтор правого смещения принимает два arg, сначала тип данных, а затем естественное количество битов для сдвига вправо.
Для<
этот вариант немного отличается; ему нужен скобчатый функтор, эквивалентный<string_sort
>operator
>[], принимающий число, соответствующее смещение символа, вместе со вторым<getlength
>функтором, чтобы получить длину строки в символах. Во всех случаях этот оператор должен вернуть целочисленный тип, который сравнивается с<operator<
>, чтобы обеспечить предполагаемый порядок (интеграторы могут быть сведены на нет, чтобы изменить их порядок).
Другими словами (кроме отрицательных поплавков, которые инвертируются как инты):
rightshift(A, n) < rightshift(B, n) -> A < B A < B -> rightshift(A, 0) < rightshift(B, 0)
integer_sort(array.begin(), array.end(), rightshift());
string_sort(array.begin(), array.end(), bracket(), getsize());
См.rightshiftsample.cppдля рабочего примера целочисленной сортировки с помощью функтора правого смещения.
И версия с фанктором сравнения для максимальной гибкости. Этот функтор должен обеспечивать тот же порядок сортировки, что и целые числа, возвращаемые правым смещением (кроме отрицательных поплавков):
rightshift(A, n) < rightshift(B, n) -> compare(A, B) compare(A, B) -> rightshift(A, 0) < rightshift(B, 0)
integer_sort(array.begin(), array.end(), negrightshift(), std::greater<DATA_TYPE>());
Примерами функторов являются:
struct lessthan { inline bool operator()(const DATA_TYPE &x, const DATA_TYPE &y) const { return x.a < y.a; } };
struct bracket { inline unsigned char operator()(const DATA_TYPE &x, size_t offset) const { return x.a[offset]; } };
struct getsize { inline size_t operator()(const DATA_TYPE &x) const{ return x.a.size(); } };
И эти функторы используются таким образом:
string_sort(array.begin(), array.end(), bracket(), getsize(), lessthan());
См.stringfunctorsample.cppдля рабочего примера сортировки струн со всеми функторами.
Алгоритм<
является гибридным алгоритмом; когда число сортируемых элементов ниже определенного числа, используется сортировка на основе сравнения. Над ним используется сортировка радикса. Алгоритм, основанный на радиксе, таким образом, разрезает проблему на мелкие части и либо полностью сортирует данные на основе их радикса, если данные кластеризованы, либо завершает сортировку вырезанных частей с помощью сортировки на основе сравнения.spreadsort
>
Алгоритм Spreadsort динамически выбирает либо сортировку на основе сравнения, либо сортировку на основе радикса при повторении, в зависимости от того, что обеспечивает лучшую производительность в худшем случае. Таким образом, худшая производительность гарантированно будет лучше.𝑶(N⋅log2(N))сравнения и𝑶(N⋅log2(K/S+S))операции, где
Это приводит к значительному улучшению производительности для большихN;<
, как правило, на 50% в 2 раза быстрее, чемstd::sort, в то время как<integer_sort
>
и _string_sort примерно в 2 раза быстрее, чемstd::sort.float_sort
>
В их описании представлены графики исполнения<
,<integer_sort
>
и<float_sort
>
.string_sort
>
Сравнения производительности и графики были сделаны на ноутбуке Core 2 Duo под управлением Windows Vista 64 с MSVC 8.0 и старом ноутбуке G4 под управлением Mac OSX с gcc.Для управления компиляцией использовался Boost bjam/b2.
Прямые сравнения производительности на более новой системе x86 под управлением Ubuntu с запасом доstd::sortпри меньших размерах ввода отключены ниже.
![]() |
Note |
---|---|
Откат кstd::sortдля меньших размеров ввода предотвращает худшую производительность, наблюдаемую на левой стороне первых двух графиков. |
<
начинает становиться быстрее, чемstd::sortпримерно на 1000 целых чисел (4000 байт), а<integer_sort
>
становится быстрее, чемstd::sortна несколько меньшем количестве байтов (всего 30 строк).string_sort
>
![]() |
Note |
---|---|
4-поточный граф имеет 4 потока, выполняющиеотдельные сортировки одновременно(не расщепляющие один сорт) в качестве теста на столкновение кэша потока и другие многопоточные проблемы производительности. |
<
раз очень похожи на<float_sort
>
раз.integer_sort
>
Используется гистограммирование с фиксированным максимальным числом расщеплений, поскольку оно уменьшает количество промахов кэша, тем самым улучшая производительность по отношению к подходу, подробно описанному воригинальной публикации SpreadSort.
Важность кэш-дружественного гистограммирования описана вArne Maus, Adaptive Left Reflex, хотя и без худшей обработки, описанной ниже.
Время, затрачиваемое на итерацию радикса, составляет:
& #119926; (N)итерации по данным
& #119926; [N]сравнение целых типов (даже для _float_sort и<
)string_sort
>
& #119926; (N)свопы
& #119926; (2S)операции бин.
Для получения& #119926; [N]худшей производительности в итерации применяется ограничениеS<= log2(N), а& #119926; [2Sстановится& #119926; (N). Для каждой такой итерации количество несортированных битов log2 (диапазон) (называемоеK) на элемент уменьшается наS. ПосколькуSуменьшается в зависимости от количества сортируемых элементов, оно может упасть с максимумаSдо минимумаSмин.
Предположение:std::sortпредполагается& #119926; [N*log2(N)], посколькуintrosortсуществует и широко используется. (Если у вас есть проблема с этим, пожалуйста, возьмите его с реализатором вашегоstd::sort; вы можете заменить рекурсивные вызовы наstd:::sortна вызовы наintrosort, если вашstd:::sortвызов библиотеки плохо реализован).
Введениене включено в этот алгоритм для простоты, и поскольку предполагается, что реализатор вызоваstd::sortзнает, что они делают.
Для поддержания минимального значения дляS (Sмин)сортировка на основе сравнения должна использоваться для сортировки, когдаn<= log2 (средний размер), гдеlog2 (средний размер) (lbs)является небольшой константой, обычно между 0 и 4, используемой для минимизации накладных расходов на один элемент. Существует небольшой угловой регистр, в котором, еслиK< Sминиn >= 2^K, то данные могут быть отсортированы в одной итерации на основе радикса сS = K(это специальное дело по умолчанию применяется только к<
). Итак, для финальной рекурсии, худшая производительность:float_sort
>
1 итерация на основе радикса, еслиK<= Sмин,
илиSмин+ lbsитерации на основе сравнения, еслиK >Sмин, ноn<= 2[Sмин+ lbs].
Таким образом, для окончательной итерации наихудшим вариантом выполнения является& #119926; [N*(Sмин+ lbs)], но еслиK >SминиN >2[Sмин+ lbs], то потребуется более 1 радикс-рекурсия.
Для второй-последней итерации можно обрабатыватьK<= Sмин* 2 + 1(если данные разделены на2(Sмин+ 1)частей) или еслиN< 2(Sмин+ lbs + 1), то быстрее вернуться кstd::sort.
В случае сортировки на основе радикса плюс рекурсия потребуется𝑶(N*(Sмин+ lbs)) + 𝑶(N) = 𝑶(N*(Sмин+ lbs + 1))наихудшее время, так какK_остаток = K_старт -иK_старт<= Sмин* 2 + 1.
Альтернативно, сортировка на основе сравнения используется, еслиN< 2(Sмин+ lbs + 1), что займет& #119926; (N*(Sмин+ lbs + 1))время.
Так или иначе& #119926; [N*(Sмин+ lbs + 1)]является наихудшим временем для второй и последней итерации, что происходит, еслиK<= Sмин* 2 +1 илиN< 2(Sмин+ lbs + 1).
Это продолжается до тех пор, покаSmin<= Smax, так что дляK_m<= K_(m-1) + Sмин+ m, гдемявляется максимальным числом итераций после того, как этот закончился, или гдеN< 2мин+ lbs + m], тогда наихудшим вариантом выполнения является& #119926; [N*(Sмин+ lbs + m)].
K_matm<= [Smax- Sмин]работает так:
K_1<= (Sмин) + Sмин+ 1<= 2Sмин+ 1
K_2<= (2Sмин+ 1) + Sмин+ 2
как сумма от 0 домсоставляетм (м + 1)/2
K_m<= (m + 1)Sмин+ m(m + 1)/2<= (Sмин+ m/2)(m + 1)
Замена в Smax- Sминдля m
K_(Smax- Sмин)<= (Sмин+ (Smax- Sмин)/2)* (Smax- Sмин+ 1)
K_(Smax- Sмин)<= (Sмин+ Smax) * (Smax- Sмин+ 1)/2
Поскольку это включает в себяSmax- Sмин+ 1итерации, это работает, чтобы разделитьKв среднем(Sмин+ Smax)/2 штук на итерацию.
Чтобы закончить проблему с этой точки, требуется& #119926; (N * (Smax- Smin))дляmитераций, плюс наихудший случай& #119926; (N * (Sмин+ lbs))для последней итерации, в общей сложности& #119926; (N * (Smax+ lbs)]время.
Когдаm >Smax- Sмин, проблема делится наSmaxштук на итерацию, илиstd::sortназывается, еслиN< 2^(m + Sмин+ lbs]. Для этого диапазона:
K_m<= K_(m - 1) + Smax, обеспечивая время выполнения
𝑶(N*((K - K_(Smax- Sмин))/Smax+ Smax+ lbs)], если рекурсивный,
или& #119926; (N * log(2^(m + Sмин+ lbs))], если сравнивать,
который упрощает дои #119926; (N * (m + Sмин+ lbs)), который заменяети #119926; (N * (m - (Smax- Sмин+ lbs)), учитывая, чтоm - (Smax- Sмин)<= (K - K_(Smax- Sмин))/Smax(в противном случае будет использоваться меньшее число итераций на основе радикса)
Также выходит& #119926; (N *((K - K_(Smax- Sмин))/Smax+ Smax+ lbs)).
Асимптотически для большихNи большихKэто упрощает:
𝑶 (N * (K/Smax+ Smax+ lbs)),
Упрощая константы, связанные с диапазономSmax- Smin, обеспечивая дополнительное& #119926; [N]max+ lbs]время выполнения поверх& #119926;производительность LSDрадикс-сорт, но без& #119926; [N]накладные расходы памяти. Для простоты, посколькуlbsявляется небольшой константой (0 может использоваться и выполняется разумно), она игнорируется при суммировании производительности в дальнейших обсуждениях. Проверяя, лучше ли сортировка на основе сравнения, Spreadsort также& #119926; [N*log(N)], в зависимости от того, что лучше, и в отличие от LSDсорт радикса, может работать намного лучше, чем в худшем случае, если данные равномерно распределены или сильно кластеризованы.
Этот анализ был для<
и<integer_sort
>
.<float_sort
>
отличается тем, чтоSmin= Smax= размер [Char_type] * 8,фунтовсоставляет 0, и чтоstd::sort's сравнение не является операцией в постоянное время, так что строго говоря<string_sort
>
время выполнения являетсяstring_sort
>
& #160; & #160;& #119926; (N * (K/Smax+ (Smaxсравнения)).
В худшем случае это получается& #119926; [N * K](гдеK- средняя длина струны в байтах), как описано дляамериканского флага сорт, который лучше, чем американский флаг.
𝑶(N * K * log(N))
Наихудший вариант для сортировки на основе сравнения.
<
и<integer_sort
>
имеют константы настройки, которые контролируют работу сортирующей радикс части этих алгоритмов. Идеальные постоянные значения для<float_sort
>
и<integer_sort
>
варьируются в зависимости от платформы, компилятора и сортируемых данных. На сегодняшний день наиболее важной константой являетсяmax_splits, которая определяет, сколько кусочков сортирующая радикс часть разделяет данные на итерацию.float_sort
>
Идеальное значениеmax_splitsзависит от размера кэша процессора L1 и составляет от 10 до 13 на многих системах. По умолчанию используется значение 11. Для в основном сортированных данных гораздо большее значение лучше, так как свопы (и, следовательно, промахи кэша) встречаются редко, но это сильно влияет на время выполнения для несортированных данных, поэтому не рекомендуется.
В некоторых системах x86, когда общее количество сортируемых элементов невелико (менее 1 миллиона или около того), идеальныеmax_splitsмогут быть существенно больше, например 17. Предполагается, что это связано с тем, что все данные вписываются в кэш L2 и промахи от кэша L1 до кэша L2 не влияют на производительность так сильно, как промахи в основной памяти. Изменение констант настройки, отличных отmax_splits, не рекомендуется, так как улучшение производительности для изменения других констант обычно незначительно.
Если вы можете позволить ему работать в течение дня и иметь не менее 1 ГБ свободной памяти, команда perl:<./tune.pl
-large
-tune
>(UNIX) или<perltune.pl-large-tune-windows
>(Windows) может использоваться для автоматической настройки этих констант. Это должно быть запущено из<libs/sortdirectory
>внутри каталога дома. Это поможет определить параметры<ideal
constants.hpp
>для вашей системы, протестировать различные дистрибутивы в файле с 20 миллионами элементов (80 МБ) и дополнительно проверить, что все процедуры сортировки правильно сортируются по различным дистрибутивам данных. Кроме того, вы можете проверить размер файла, который вас больше всего интересует<./tune.plnumber
-tune
>(UNIX) или<perltune.plnumber
-tune
-windows
>(Windows). Замените количество элементов, которые вы хотите протестировать<number
>. В противном случае, просто используйте варианты, которые он предлагает, они приличные. При настройках по умолчанию<./tune.pl
-tune
>(UNIX)<perltune.pl-tune-windows
>(Windows) скрипт будет работать часами (менее суток), но может не выбрать правильныйmax_splits, если он превышает 10. Кроме того, вы можете добавить опцию<-small
>, чтобы она занимала всего несколько минут, настраивая для меньших размеров векторов (сто тысяч элементов), но результирующие константы могут не подходить для больших файлов (см. выше примечание оmax_splitsв Windows).
Сценарий настройки также может быть использован только для проверки того, что сортировка работает правильно в вашей системе, и посмотреть, сколько ускорения она получает, опуская опцию «настройка». Это происходит в конце тюнинга. Для запуска по умолчанию потребуется около часа, чтобы получить точные результаты на тестовых векторах приличного размера.<./tune.pl-small
>(UNIX)<perl
tune.pl-small
-windows
>(Windows) — более быстрый вариант, который тестирует на меньших векторах и не так точен.
Если во время настройки возникают какие-либо различия, позвоните<tune.pl
>с<-debug
>log_file_name
>. Если полученный файл журнала содержит проблемы с компиляцией или разрешениями, это, вероятно, проблема с вашей установкой. Если вы столкнулись с какой-либо другой ошибкой (или различиями в результате), пожалуйста, отправьте их автору библиотеки по адресу spreadsort@gmail.com. Кроме того, полезно использовать застежки<input.txt
>.
Последний пересмотр: 21 сентября 2016 года в 14:49:33 GMT |
Статья Boost.Sort раздела Boost.Sort может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: ::
реклама |