integer_sort.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. // Details for templated Spreadsort-based integer_sort.
  2. // Copyright Steven J. Ross 2001 - 2014.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. // See http://www.boost.org/libs/sort for library home page.
  7. /*
  8. Some improvements suggested by:
  9. Phil Endecott and Frank Gennari
  10. */
  11. #ifndef BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
  12. #define BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
  13. #include <algorithm>
  14. #include <vector>
  15. #include <limits>
  16. #include <functional>
  17. #include <boost/static_assert.hpp>
  18. #include <boost/serialization/static_warning.hpp>
  19. #include <boost/utility/enable_if.hpp>
  20. #include <boost/sort/spreadsort/detail/constants.hpp>
  21. #include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
  22. #include <boost/cstdint.hpp>
  23. namespace boost {
  24. namespace sort {
  25. namespace spreadsort {
  26. namespace detail {
  27. // Return true if the list is sorted. Otherwise, find the minimum and
  28. // maximum using <.
  29. template <class RandomAccessIter>
  30. inline bool
  31. is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
  32. RandomAccessIter & max, RandomAccessIter & min)
  33. {
  34. min = max = current;
  35. //This assumes we have more than 1 element based on prior checks.
  36. while (!(*(current + 1) < *current)) {
  37. //If everything is in sorted order, return
  38. if (++current == last - 1)
  39. return true;
  40. }
  41. //The maximum is the last sorted element
  42. max = current;
  43. //Start from the first unsorted element
  44. while (++current < last) {
  45. if (*max < *current)
  46. max = current;
  47. else if (*current < *min)
  48. min = current;
  49. }
  50. return false;
  51. }
  52. // Return true if the list is sorted. Otherwise, find the minimum and
  53. // maximum.
  54. // Use a user-defined comparison operator
  55. template <class RandomAccessIter, class Compare>
  56. inline bool
  57. is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
  58. RandomAccessIter & max, RandomAccessIter & min, Compare comp)
  59. {
  60. min = max = current;
  61. while (!comp(*(current + 1), *current)) {
  62. //If everything is in sorted order, return
  63. if (++current == last - 1)
  64. return true;
  65. }
  66. //The maximum is the last sorted element
  67. max = current;
  68. while (++current < last) {
  69. if (comp(*max, *current))
  70. max = current;
  71. else if (comp(*current, *min))
  72. min = current;
  73. }
  74. return false;
  75. }
  76. //Gets a non-negative right bit shift to operate as a logarithmic divisor
  77. template<unsigned log_mean_bin_size>
  78. inline int
  79. get_log_divisor(size_t count, int log_range)
  80. {
  81. int log_divisor;
  82. //If we can finish in one iteration without exceeding either
  83. //(2 to the max_finishing_splits) or n bins, do so
  84. if ((log_divisor = log_range - rough_log_2_size(count)) <= 0 &&
  85. log_range <= max_finishing_splits)
  86. log_divisor = 0;
  87. else {
  88. //otherwise divide the data into an optimized number of pieces
  89. log_divisor += log_mean_bin_size;
  90. //Cannot exceed max_splits or cache misses slow down bin lookups
  91. if ((log_range - log_divisor) > max_splits)
  92. log_divisor = log_range - max_splits;
  93. }
  94. return log_divisor;
  95. }
  96. //Implementation for recursive integer sorting
  97. template <class RandomAccessIter, class Div_type, class Size_type>
  98. inline void
  99. spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
  100. std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
  101. , size_t *bin_sizes)
  102. {
  103. //This step is roughly 10% of runtime, but it helps avoid worst-case
  104. //behavior and improve behavior with real data
  105. //If you know the maximum and minimum ahead of time, you can pass those
  106. //values in and skip this step for the first iteration
  107. RandomAccessIter max, min;
  108. if (is_sorted_or_find_extremes(first, last, max, min))
  109. return;
  110. RandomAccessIter * target_bin;
  111. unsigned log_divisor = get_log_divisor<int_log_mean_bin_size>(
  112. last - first, rough_log_2_size(Size_type((*max >> 0) - (*min >> 0))));
  113. Div_type div_min = *min >> log_divisor;
  114. Div_type div_max = *max >> log_divisor;
  115. unsigned bin_count = unsigned(div_max - div_min) + 1;
  116. unsigned cache_end;
  117. RandomAccessIter * bins =
  118. size_bins(bin_sizes, bin_cache, cache_offset, cache_end, bin_count);
  119. //Calculating the size of each bin; this takes roughly 10% of runtime
  120. for (RandomAccessIter current = first; current != last;)
  121. bin_sizes[size_t((*(current++) >> log_divisor) - div_min)]++;
  122. //Assign the bin positions
  123. bins[0] = first;
  124. for (unsigned u = 0; u < bin_count - 1; u++)
  125. bins[u + 1] = bins[u] + bin_sizes[u];
  126. RandomAccessIter nextbinstart = first;
  127. //Swap into place
  128. //This dominates runtime, mostly in the swap and bin lookups
  129. for (unsigned u = 0; u < bin_count - 1; ++u) {
  130. RandomAccessIter * local_bin = bins + u;
  131. nextbinstart += bin_sizes[u];
  132. //Iterating over each element in this bin
  133. for (RandomAccessIter current = *local_bin; current < nextbinstart;
  134. ++current) {
  135. //Swapping elements in current into place until the correct
  136. //element has been swapped in
  137. for (target_bin = (bins + ((*current >> log_divisor) - div_min));
  138. target_bin != local_bin;
  139. target_bin = bins + ((*current >> log_divisor) - div_min)) {
  140. //3-way swap; this is about 1% faster than a 2-way swap
  141. //The main advantage is less copies are involved per item
  142. //put in the correct place
  143. typename std::iterator_traits<RandomAccessIter>::value_type tmp;
  144. RandomAccessIter b = (*target_bin)++;
  145. RandomAccessIter * b_bin = bins + ((*b >> log_divisor) - div_min);
  146. if (b_bin != local_bin) {
  147. RandomAccessIter c = (*b_bin)++;
  148. tmp = *c;
  149. *c = *b;
  150. }
  151. else
  152. tmp = *b;
  153. *b = *current;
  154. *current = tmp;
  155. }
  156. }
  157. *local_bin = nextbinstart;
  158. }
  159. bins[bin_count - 1] = last;
  160. //If we've bucketsorted, the array is sorted and we should skip recursion
  161. if (!log_divisor)
  162. return;
  163. //log_divisor is the remaining range; calculating the comparison threshold
  164. size_t max_count =
  165. get_min_count<int_log_mean_bin_size, int_log_min_split_count,
  166. int_log_finishing_count>(log_divisor);
  167. //Recursing
  168. RandomAccessIter lastPos = first;
  169. for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
  170. ++u) {
  171. Size_type count = bin_cache[u] - lastPos;
  172. //don't sort unless there are at least two items to Compare
  173. if (count < 2)
  174. continue;
  175. //using boost::sort::pdqsort if its worst-case is better
  176. if (count < max_count)
  177. boost::sort::pdqsort(lastPos, bin_cache[u]);
  178. else
  179. spreadsort_rec<RandomAccessIter, Div_type, Size_type>(lastPos,
  180. bin_cache[u],
  181. bin_cache,
  182. cache_end,
  183. bin_sizes);
  184. }
  185. }
  186. //Generic bitshift-based 3-way swapping code
  187. template <class RandomAccessIter, class Div_type, class Right_shift>
  188. inline void inner_swap_loop(RandomAccessIter * bins,
  189. const RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
  190. , const unsigned log_divisor, const Div_type div_min)
  191. {
  192. RandomAccessIter * local_bin = bins + ii;
  193. for (RandomAccessIter current = *local_bin; current < next_bin_start;
  194. ++current) {
  195. for (RandomAccessIter * target_bin =
  196. (bins + (rshift(*current, log_divisor) - div_min));
  197. target_bin != local_bin;
  198. target_bin = bins + (rshift(*current, log_divisor) - div_min)) {
  199. typename std::iterator_traits<RandomAccessIter>::value_type tmp;
  200. RandomAccessIter b = (*target_bin)++;
  201. RandomAccessIter * b_bin =
  202. bins + (rshift(*b, log_divisor) - div_min);
  203. //Three-way swap; if the item to be swapped doesn't belong
  204. //in the current bin, swap it to where it belongs
  205. if (b_bin != local_bin) {
  206. RandomAccessIter c = (*b_bin)++;
  207. tmp = *c;
  208. *c = *b;
  209. }
  210. //Note: we could increment current once the swap is done in this case
  211. //but that seems to impair performance
  212. else
  213. tmp = *b;
  214. *b = *current;
  215. *current = tmp;
  216. }
  217. }
  218. *local_bin = next_bin_start;
  219. }
  220. //Standard swapping wrapper for ascending values
  221. template <class RandomAccessIter, class Div_type, class Right_shift>
  222. inline void swap_loop(RandomAccessIter * bins,
  223. RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
  224. , const size_t *bin_sizes
  225. , const unsigned log_divisor, const Div_type div_min)
  226. {
  227. next_bin_start += bin_sizes[ii];
  228. inner_swap_loop<RandomAccessIter, Div_type, Right_shift>(bins,
  229. next_bin_start, ii, rshift, log_divisor, div_min);
  230. }
  231. //Functor implementation for recursive sorting
  232. template <class RandomAccessIter, class Div_type, class Right_shift,
  233. class Compare, class Size_type, unsigned log_mean_bin_size,
  234. unsigned log_min_split_count, unsigned log_finishing_count>
  235. inline void
  236. spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
  237. std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
  238. , size_t *bin_sizes, Right_shift rshift, Compare comp)
  239. {
  240. RandomAccessIter max, min;
  241. if (is_sorted_or_find_extremes(first, last, max, min, comp))
  242. return;
  243. unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
  244. rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
  245. Div_type div_min = rshift(*min, log_divisor);
  246. Div_type div_max = rshift(*max, log_divisor);
  247. unsigned bin_count = unsigned(div_max - div_min) + 1;
  248. unsigned cache_end;
  249. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  250. cache_end, bin_count);
  251. //Calculating the size of each bin
  252. for (RandomAccessIter current = first; current != last;)
  253. bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
  254. bins[0] = first;
  255. for (unsigned u = 0; u < bin_count - 1; u++)
  256. bins[u + 1] = bins[u] + bin_sizes[u];
  257. //Swap into place
  258. RandomAccessIter next_bin_start = first;
  259. for (unsigned u = 0; u < bin_count - 1; ++u)
  260. swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, next_bin_start,
  261. u, rshift, bin_sizes, log_divisor, div_min);
  262. bins[bin_count - 1] = last;
  263. //If we've bucketsorted, the array is sorted
  264. if (!log_divisor)
  265. return;
  266. //Recursing
  267. size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
  268. log_finishing_count>(log_divisor);
  269. RandomAccessIter lastPos = first;
  270. for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
  271. ++u) {
  272. size_t count = bin_cache[u] - lastPos;
  273. if (count < 2)
  274. continue;
  275. if (count < max_count)
  276. boost::sort::pdqsort(lastPos, bin_cache[u], comp);
  277. else
  278. spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
  279. Size_type, log_mean_bin_size, log_min_split_count, log_finishing_count>
  280. (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp);
  281. }
  282. }
  283. //Functor implementation for recursive sorting with only Shift overridden
  284. template <class RandomAccessIter, class Div_type, class Right_shift,
  285. class Size_type, unsigned log_mean_bin_size,
  286. unsigned log_min_split_count, unsigned log_finishing_count>
  287. inline void
  288. spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
  289. std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
  290. , size_t *bin_sizes, Right_shift rshift)
  291. {
  292. RandomAccessIter max, min;
  293. if (is_sorted_or_find_extremes(first, last, max, min))
  294. return;
  295. unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
  296. rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
  297. Div_type div_min = rshift(*min, log_divisor);
  298. Div_type div_max = rshift(*max, log_divisor);
  299. unsigned bin_count = unsigned(div_max - div_min) + 1;
  300. unsigned cache_end;
  301. RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
  302. cache_end, bin_count);
  303. //Calculating the size of each bin
  304. for (RandomAccessIter current = first; current != last;)
  305. bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
  306. bins[0] = first;
  307. for (unsigned u = 0; u < bin_count - 1; u++)
  308. bins[u + 1] = bins[u] + bin_sizes[u];
  309. //Swap into place
  310. RandomAccessIter nextbinstart = first;
  311. for (unsigned ii = 0; ii < bin_count - 1; ++ii)
  312. swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, nextbinstart,
  313. ii, rshift, bin_sizes, log_divisor, div_min);
  314. bins[bin_count - 1] = last;
  315. //If we've bucketsorted, the array is sorted
  316. if (!log_divisor)
  317. return;
  318. //Recursing
  319. size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
  320. log_finishing_count>(log_divisor);
  321. RandomAccessIter lastPos = first;
  322. for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
  323. ++u) {
  324. size_t count = bin_cache[u] - lastPos;
  325. if (count < 2)
  326. continue;
  327. if (count < max_count)
  328. boost::sort::pdqsort(lastPos, bin_cache[u]);
  329. else
  330. spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type,
  331. log_mean_bin_size, log_min_split_count, log_finishing_count>(lastPos,
  332. bin_cache[u], bin_cache, cache_end, bin_sizes, rshift);
  333. }
  334. }
  335. //Holds the bin vector and makes the initial recursive call
  336. template <class RandomAccessIter, class Div_type>
  337. //Only use spreadsort if the integer can fit in a size_t
  338. inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
  339. void >::type
  340. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
  341. {
  342. size_t bin_sizes[1 << max_finishing_splits];
  343. std::vector<RandomAccessIter> bin_cache;
  344. spreadsort_rec<RandomAccessIter, Div_type, size_t>(first, last,
  345. bin_cache, 0, bin_sizes);
  346. }
  347. //Holds the bin vector and makes the initial recursive call
  348. template <class RandomAccessIter, class Div_type>
  349. //Only use spreadsort if the integer can fit in a uintmax_t
  350. inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
  351. && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  352. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
  353. {
  354. size_t bin_sizes[1 << max_finishing_splits];
  355. std::vector<RandomAccessIter> bin_cache;
  356. spreadsort_rec<RandomAccessIter, Div_type, boost::uintmax_t>(first,
  357. last, bin_cache, 0, bin_sizes);
  358. }
  359. template <class RandomAccessIter, class Div_type>
  360. inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
  361. || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  362. //defaulting to boost::sort::pdqsort when integer_sort won't work
  363. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
  364. {
  365. //Warning that we're using boost::sort::pdqsort, even though integer_sort was called
  366. BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
  367. boost::sort::pdqsort(first, last);
  368. }
  369. //Same for the full functor version
  370. template <class RandomAccessIter, class Div_type, class Right_shift,
  371. class Compare>
  372. //Only use spreadsort if the integer can fit in a size_t
  373. inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
  374. void >::type
  375. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  376. Right_shift shift, Compare comp)
  377. {
  378. size_t bin_sizes[1 << max_finishing_splits];
  379. std::vector<RandomAccessIter> bin_cache;
  380. spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
  381. size_t, int_log_mean_bin_size, int_log_min_split_count,
  382. int_log_finishing_count>
  383. (first, last, bin_cache, 0, bin_sizes, shift, comp);
  384. }
  385. template <class RandomAccessIter, class Div_type, class Right_shift,
  386. class Compare>
  387. //Only use spreadsort if the integer can fit in a uintmax_t
  388. inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
  389. && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  390. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  391. Right_shift shift, Compare comp)
  392. {
  393. size_t bin_sizes[1 << max_finishing_splits];
  394. std::vector<RandomAccessIter> bin_cache;
  395. spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
  396. boost::uintmax_t, int_log_mean_bin_size,
  397. int_log_min_split_count, int_log_finishing_count>
  398. (first, last, bin_cache, 0, bin_sizes, shift, comp);
  399. }
  400. template <class RandomAccessIter, class Div_type, class Right_shift,
  401. class Compare>
  402. inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
  403. || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  404. //defaulting to boost::sort::pdqsort when integer_sort won't work
  405. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  406. Right_shift shift, Compare comp)
  407. {
  408. //Warning that we're using boost::sort::pdqsort, even though integer_sort was called
  409. BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
  410. boost::sort::pdqsort(first, last, comp);
  411. }
  412. //Same for the right shift version
  413. template <class RandomAccessIter, class Div_type, class Right_shift>
  414. //Only use spreadsort if the integer can fit in a size_t
  415. inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
  416. void >::type
  417. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  418. Right_shift shift)
  419. {
  420. size_t bin_sizes[1 << max_finishing_splits];
  421. std::vector<RandomAccessIter> bin_cache;
  422. spreadsort_rec<RandomAccessIter, Div_type, Right_shift, size_t,
  423. int_log_mean_bin_size, int_log_min_split_count,
  424. int_log_finishing_count>
  425. (first, last, bin_cache, 0, bin_sizes, shift);
  426. }
  427. template <class RandomAccessIter, class Div_type, class Right_shift>
  428. //Only use spreadsort if the integer can fit in a uintmax_t
  429. inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
  430. && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  431. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  432. Right_shift shift)
  433. {
  434. size_t bin_sizes[1 << max_finishing_splits];
  435. std::vector<RandomAccessIter> bin_cache;
  436. spreadsort_rec<RandomAccessIter, Div_type, Right_shift,
  437. boost::uintmax_t, int_log_mean_bin_size,
  438. int_log_min_split_count, int_log_finishing_count>
  439. (first, last, bin_cache, 0, bin_sizes, shift);
  440. }
  441. template <class RandomAccessIter, class Div_type, class Right_shift>
  442. inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
  443. || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
  444. //defaulting to boost::sort::pdqsort when integer_sort won't work
  445. integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
  446. Right_shift shift)
  447. {
  448. //Warning that we're using boost::sort::pdqsort, even though integer_sort was called
  449. BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
  450. boost::sort::pdqsort(first, last);
  451. }
  452. }
  453. }
  454. }
  455. }
  456. #endif