parallel_stable_sort.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. //----------------------------------------------------------------------------
  2. /// @file parallel_stable_sort.hpp
  3. /// @brief This file contains the class parallel_stable_sort
  4. ///
  5. /// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n
  6. /// Distributed under the Boost Software License, Version 1.0.\n
  7. /// ( See accompanying file LICENSE_1_0.txt or copy at
  8. /// http://www.boost.org/LICENSE_1_0.txt )
  9. /// @version 0.1
  10. ///
  11. /// @remarks
  12. //-----------------------------------------------------------------------------
  13. #ifndef __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_STABLE_SORT_HPP
  14. #define __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_STABLE_SORT_HPP
  15. #include <boost/sort/sample_sort/sample_sort.hpp>
  16. #include <boost/sort/common/util/traits.hpp>
  17. #include <functional>
  18. #include <future>
  19. #include <iterator>
  20. #include <memory>
  21. #include <type_traits>
  22. #include <vector>
  23. namespace boost
  24. {
  25. namespace sort
  26. {
  27. namespace stable_detail
  28. {
  29. //---------------------------------------------------------------------------
  30. // USING SENTENCES
  31. //---------------------------------------------------------------------------
  32. namespace bsc = boost::sort::common;
  33. namespace bss = boost::sort::spin_detail;
  34. using bsc::range;
  35. using bsc::merge_half;
  36. using boost::sort::sample_detail::sample_sort;
  37. //
  38. ///---------------------------------------------------------------------------
  39. /// @struct parallel_stable_sort
  40. /// @brief This a structure for to implement a parallel stable sort, exception
  41. /// safe
  42. //----------------------------------------------------------------------------
  43. template <class Iter_t, class Compare = compare_iter <Iter_t> >
  44. struct parallel_stable_sort
  45. {
  46. //-------------------------------------------------------------------------
  47. // DEFINITIONS
  48. //-------------------------------------------------------------------------
  49. typedef value_iter<Iter_t> value_t;
  50. //-------------------------------------------------------------------------
  51. // VARIABLES
  52. //-------------------------------------------------------------------------
  53. // Number of elements to sort
  54. size_t nelem;
  55. // Pointer to the auxiliary memory needed for the algorithm
  56. value_t *ptr;
  57. // Minimal number of elements for to be sorted in parallel mode
  58. const size_t nelem_min = 1 << 16;
  59. //------------------------------------------------------------------------
  60. // F U N C T I O N S
  61. //------------------------------------------------------------------------
  62. parallel_stable_sort (Iter_t first, Iter_t last)
  63. : parallel_stable_sort (first, last, Compare(),
  64. std::thread::hardware_concurrency()) { };
  65. parallel_stable_sort (Iter_t first, Iter_t last, Compare cmp)
  66. : parallel_stable_sort (first, last, cmp,
  67. std::thread::hardware_concurrency()) { };
  68. parallel_stable_sort (Iter_t first, Iter_t last, uint32_t num_thread)
  69. : parallel_stable_sort (first, last, Compare(), num_thread) { };
  70. parallel_stable_sort (Iter_t first, Iter_t last, Compare cmp,
  71. uint32_t num_thread);
  72. //
  73. //-----------------------------------------------------------------------------
  74. // function : destroy_all
  75. /// @brief The utility is to destroy the temporary buffer used in the
  76. /// sorting process
  77. //-----------------------------------------------------------------------------
  78. void destroy_all()
  79. {
  80. if (ptr != nullptr) std::return_temporary_buffer(ptr);
  81. };
  82. //
  83. //-----------------------------------------------------------------------------
  84. // function :~parallel_stable_sort
  85. /// @brief destructor of the class. The utility is to destroy the temporary
  86. /// buffer used in the sorting process
  87. //-----------------------------------------------------------------------------
  88. ~parallel_stable_sort() {destroy_all(); } ;
  89. };
  90. // end struct parallel_stable_sort
  91. //
  92. //############################################################################
  93. // ##
  94. // ##
  95. // N O N I N L I N E F U N C T I O N S ##
  96. // ##
  97. // ##
  98. //############################################################################
  99. //
  100. //-----------------------------------------------------------------------------
  101. // function : parallel_stable_sort
  102. /// @brief constructor of the class
  103. ///
  104. /// @param first : iterator to the first element of the range to sort
  105. /// @param last : iterator after the last element to the range to sort
  106. /// @param comp : object for to compare two elements pointed by Iter_t
  107. /// iterators
  108. /// @param nthread : Number of threads to use in the process. When this value
  109. /// is lower than 2, the sorting is done with 1 thread
  110. //-----------------------------------------------------------------------------
  111. template <class Iter_t, class Compare>
  112. parallel_stable_sort <Iter_t, Compare>
  113. ::parallel_stable_sort (Iter_t first, Iter_t last, Compare comp,
  114. uint32_t nthread) : nelem(0), ptr(nullptr)
  115. {
  116. range<Iter_t> range_initial(first, last);
  117. assert(range_initial.valid());
  118. nelem = range_initial.size();
  119. size_t nptr = (nelem + 1) >> 1;
  120. if (nelem < nelem_min or nthread < 2)
  121. {
  122. bss::spinsort<Iter_t, Compare>
  123. (range_initial.first, range_initial.last, comp);
  124. return;
  125. };
  126. //------------------- check if sort --------------------------------------
  127. bool sw = true;
  128. for (Iter_t it1 = first, it2 = first + 1;
  129. it2 != last and (sw = not comp(*it2, *it1)); it1 = it2++);
  130. if (sw) return;
  131. //------------------- check if reverse sort ---------------------------
  132. sw = true;
  133. for (Iter_t it1 = first, it2 = first + 1;
  134. it2 != last and (sw = comp(*it2, *it1)); it1 = it2++);
  135. if (sw)
  136. {
  137. size_t nelem2 = nelem >> 1;
  138. Iter_t it1 = first, it2 = last - 1;
  139. for (size_t i = 0; i < nelem2; ++i)
  140. std::swap(*(it1++), *(it2--));
  141. return;
  142. };
  143. ptr = std::get_temporary_buffer<value_t>(nptr).first;
  144. if (ptr == nullptr) throw std::bad_alloc();
  145. //---------------------------------------------------------------------
  146. // Parallel Process
  147. //---------------------------------------------------------------------
  148. range<Iter_t> range_first(range_initial.first, range_initial.first + nptr);
  149. range<Iter_t> range_second(range_initial.first + nptr, range_initial.last);
  150. range<value_t *> range_buffer(ptr, ptr + nptr);
  151. try
  152. {
  153. sample_sort<Iter_t, Compare>
  154. (range_initial.first, range_initial.first + nptr,
  155. comp, nthread, range_buffer);
  156. } catch (std::bad_alloc &)
  157. {
  158. destroy_all();
  159. throw std::bad_alloc();
  160. };
  161. try
  162. {
  163. sample_sort<Iter_t, Compare>
  164. (range_initial.first + nptr,
  165. range_initial.last, comp, nthread, range_buffer);
  166. } catch (std::bad_alloc &)
  167. {
  168. destroy_all();
  169. throw std::bad_alloc();
  170. };
  171. range_buffer = move_forward(range_buffer, range_first);
  172. range_initial = merge_half(range_initial, range_buffer, range_second, comp);
  173. }; // end of constructor
  174. //
  175. //****************************************************************************
  176. };// End namespace stable_detail
  177. //****************************************************************************
  178. //
  179. //---------------------------------------------------------------------------
  180. // USING SENTENCES
  181. //---------------------------------------------------------------------------
  182. namespace bsc = boost::sort::common;
  183. namespace bscu = bsc::util;
  184. namespace bss = boost::sort::spin_detail;
  185. using bsc::range;
  186. using bsc::merge_half;
  187. //
  188. //############################################################################
  189. // ##
  190. // ##
  191. // P A R A L L E L _ S T A B L E _ S O R T ##
  192. // ##
  193. // ##
  194. //############################################################################
  195. //
  196. //-----------------------------------------------------------------------------
  197. // function : parallel_stable_sort
  198. /// @brief : parallel stable sort with 2 parameters
  199. ///
  200. /// @param first : iterator to the first element of the range to sort
  201. /// @param last : iterator after the last element to the range to sort
  202. //-----------------------------------------------------------------------------
  203. template<class Iter_t>
  204. void parallel_stable_sort(Iter_t first, Iter_t last)
  205. {
  206. typedef bscu::compare_iter<Iter_t> Compare;
  207. stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last);
  208. };
  209. //
  210. //-----------------------------------------------------------------------------
  211. // function : parallel_stable_sort
  212. /// @brief parallel stable sort with 3 parameters. The third is the number
  213. /// of threads
  214. ///
  215. /// @param first : iterator to the first element of the range to sort
  216. /// @param last : iterator after the last element to the range to sort
  217. /// @param nthread : Number of threads to use in the process. When this value
  218. /// is lower than 2, the sorting is done with 1 thread
  219. //-----------------------------------------------------------------------------
  220. template<class Iter_t>
  221. void parallel_stable_sort(Iter_t first, Iter_t last, uint32_t nthread)
  222. {
  223. typedef bscu::compare_iter<Iter_t> Compare;
  224. stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last, nthread);
  225. };
  226. //
  227. //-----------------------------------------------------------------------------
  228. // function : parallel_stable_sort
  229. /// @brief : parallel stable sort with 3 parameters. The thisrd is the
  230. /// comparison object
  231. ///
  232. /// @param first : iterator to the first element of the range to sort
  233. /// @param last : iterator after the last element to the range to sort
  234. /// @param comp : object for to compare two elements pointed by Iter_t
  235. /// iterators
  236. //-----------------------------------------------------------------------------
  237. template <class Iter_t, class Compare,
  238. bscu::enable_if_not_integral<Compare> * = nullptr>
  239. void parallel_stable_sort(Iter_t first, Iter_t last, Compare comp)
  240. {
  241. stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last, comp);
  242. };
  243. //
  244. //-----------------------------------------------------------------------------
  245. // function : parallel_stable_sort
  246. /// @brief : parallel stable sort with 3 parameters.
  247. ///
  248. /// @param first : iterator to the first element of the range to sort
  249. /// @param last : iterator after the last element to the range to sort
  250. /// @param comp : object for to compare two elements pointed by Iter_t
  251. /// iterators
  252. /// @param nthread : Number of threads to use in the process. When this value
  253. /// is lower than 2, the sorting is done with 1 thread
  254. //-----------------------------------------------------------------------------
  255. template<class Iter_t, class Compare>
  256. void parallel_stable_sort (Iter_t first, Iter_t last, Compare comp,
  257. uint32_t nthread)
  258. {
  259. stable_detail::parallel_stable_sort<Iter_t, Compare>
  260. (first, last, comp, nthread);
  261. }
  262. //
  263. //****************************************************************************
  264. };// End namespace sort
  265. };// End namespace boost
  266. //****************************************************************************
  267. //
  268. #endif