Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Boost.Sort

Boost , Boost.Sort ,

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Next

Boost.Sort

Steven Ross

Распространяется по лицензииBoost Software License, версия 1.0.

Начало. Библиотека сортировки обеспечивает общую реализацию высокоскоростных алгоритмов сортировки, которые превосходят таковые в стандарте C++ как в среднем, так и в худшем случае, когда в списке есть более 1000 элементов для сортировки.

Они возвращаются кSTL std::sortна небольших наборах данных.

[Warning] Warning

Все эти алгоритмы работают только наитераторах случайного доступа..

Они являются гибридами, использующими как радикс, так и сортировку на основе сравнения, специализированную для сортировки общих типов данных, таких как целые числа, поплавки и строки.

Эти алгоритмы кодируются общим способом и принимают функторы, что позволяет им сортировать любой объект, который может быть обработан, как эти основные типы данных. В случае<string_sort>, это включает в себя все с определенным строгим слабым упорядочением, чтоstd::sortможет сортировать, но написание эффективных функторов для некоторых сложных ключевых типов может не стоить дополнительных усилий по сравнению с просто использованиемstd::sort, в зависимости от того, насколько важна скорость для вашего приложения. Использование образцов доступно в каталоге примеров.

В отличие от многих алгоритмов, основанных на радиксе, основной алгоритм<spreadsort>разработан вокругнаихудшей производительности. Он работает лучше на кусочных данных (где он не широко распространен), так что на реальных данных он может работать значительно лучше, чем на случайных данных. Концептуально<spreadsort>может сортировать любые данные, для которых может быть определен абсолютный порядок, и<string_sort>достаточно гибок, чтобы это было возможно.

<spreadsort>— [] — [] — [[]:

  1. Большое количество элементов для сортировкиN>= 10000.
  2. Функция медленного сравнения (например, числа с плавающей запятой на процессорах x86 или строках).
  3. Большие элементы данных (например, ключ + данные, отсортированные по ключу).
  4. Полностью отсортированные данные, когда<spreadsort>имеет оптимизацию, чтобы выйти на ранней стадии в этом случае.

<spreadsort>— [] — [] — [[]:

  1. Данные отсортированы в обратном порядке. Обаstd::sortи<spreadsort>быстрее на данных обратного порядка, чем на рандомизированных данных, ноstd:::sortв этом особом случае ускоряется больше.
  2. Очень малые объемы данных (1000 элементов). По этой причине существует откат в<spreadsort>кstd::sort, если размер ввода меньше 1000, поэтому производительность идентична для небольших объемов данных на практике.

Эти функции определены в<namespace boost::sort::spreadsort>.

[Tip] Tip

В подъёме. Сортировать C++ В справочном разделе нажмите на соответствующую перегрузку, например<float_sort(RandomAccessIter,RandomAccessIter,Right_shift,Compare);>, чтобы получить полную информацию об этой перегрузке.

Каждый из<integer_sort>,<float_sort>и<string_sort>имеет 3 основных варианта: Базовая версия, которая берет первый итератор и последний итератор, какstd::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— число сортируемых элементов,
  • К— длина в битах ключа, и
  • S— постоянная величина.

Это приводит к значительному улучшению производительности для большихN;<integer_sort>, как правило, на 50% в 2 раза быстрее, чемstd::sort, в то время как<float_sort>и _string_sort примерно в 2 раза быстрее, чемstd::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] Note

Откат кstd::sortдля меньших размеров ввода предотвращает худшую производительность, наблюдаемую на левой стороне первых двух графиков.

<integer_sort>начинает становиться быстрее, чемstd::sortпримерно на 1000 целых чисел (4000 байт), а<string_sort>становится быстрее, чемstd::sortна несколько меньшем количестве байтов (всего 30 строк).

[Note] 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>.<string_sort>отличается тем, чтоSmin= Smax= размер [Char_type] * 8,фунтовсоставляет 0, и чтоstd::sort's сравнение не является операцией в постоянное время, так что строго говоря<string_sort>время выполнения является

& #160; & #160;& #119926; (N * (K/Smax+ (Smaxсравнения)).

В худшем случае это получается& #119926; [N * K](гдеK- средняя длина струны в байтах), как описано дляамериканского флага сорт, который лучше, чем американский флаг.

   𝑶(N * K * log(N))

Наихудший вариант для сортировки на основе сравнения.

<integer_sort>и<float_sort>имеют константы настройки, которые контролируют работу сортирующей радикс части этих алгоритмов. Идеальные постоянные значения для<integer_sort>и<float_sort>варьируются в зависимости от платформы, компилятора и сортируемых данных. На сегодняшний день наиболее важной константой являетсяmax_splits, которая определяет, сколько кусочков сортирующая радикс часть разделяет данные на итерацию.

Идеальное значение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


Next

Статья Boost.Sort раздела Boost.Sort может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-04 15:49:48/0.01049017906189/0