Warning, file /include/oneapi/tbb/parallel_sort.h was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #ifndef __TBB_parallel_sort_H
0018 #define __TBB_parallel_sort_H
0019
0020 #include "detail/_namespace_injection.h"
0021 #include "parallel_for.h"
0022 #include "blocked_range.h"
0023 #include "profiling.h"
0024
0025 #include <algorithm>
0026 #include <iterator>
0027 #include <functional>
0028 #include <cstddef>
0029
0030 namespace tbb {
0031 namespace detail {
0032 #if __TBB_CPP20_CONCEPTS_PRESENT
0033 inline namespace d0 {
0034
0035
0036 template <typename Compare, typename Iterator>
0037 concept compare = requires( const std::remove_reference_t<Compare>& comp, typename std::iterator_traits<Iterator>::reference value ) {
0038
0039 { comp(typename std::iterator_traits<Iterator>::reference(value),
0040 typename std::iterator_traits<Iterator>::reference(value)) } -> std::convertible_to<bool>;
0041 };
0042
0043
0044 template <typename T>
0045 concept less_than_comparable = requires( const std::remove_reference_t<T>& lhs,
0046 const std::remove_reference_t<T>& rhs ) {
0047 { lhs < rhs } -> boolean_testable;
0048 };
0049
0050 }
0051 #endif
0052 namespace d1 {
0053
0054
0055
0056
0057
0058 template<typename RandomAccessIterator, typename Compare>
0059 class quick_sort_range {
0060 std::size_t median_of_three( const RandomAccessIterator& array, std::size_t l, std::size_t m, std::size_t r ) const {
0061 return comp(array[l], array[m]) ? ( comp(array[m], array[r]) ? m : ( comp(array[l], array[r]) ? r : l ) )
0062 : ( comp(array[r], array[m]) ? m : ( comp(array[r], array[l]) ? r : l ) );
0063 }
0064
0065 std::size_t pseudo_median_of_nine( const RandomAccessIterator& array, const quick_sort_range& range ) const {
0066 std::size_t offset = range.size / 8u;
0067 return median_of_three(array,
0068 median_of_three(array, 0 , offset, offset * 2),
0069 median_of_three(array, offset * 3, offset * 4, offset * 5),
0070 median_of_three(array, offset * 6, offset * 7, range.size - 1));
0071
0072 }
0073
0074 std::size_t split_range( quick_sort_range& range ) {
0075 RandomAccessIterator array = range.begin;
0076 RandomAccessIterator first_element = range.begin;
0077 std::size_t m = pseudo_median_of_nine(array, range);
0078 if( m != 0 ) std::iter_swap(array, array + m);
0079
0080 std::size_t i = 0;
0081 std::size_t j = range.size;
0082
0083 for(;;) {
0084 __TBB_ASSERT( i < j, nullptr );
0085
0086 do {
0087 --j;
0088 __TBB_ASSERT( i <= j, "bad ordering relation?" );
0089 } while( comp(*first_element, array[j]) );
0090 do {
0091 __TBB_ASSERT( i <= j, nullptr );
0092 if( i == j ) goto partition;
0093 ++i;
0094 } while( comp(array[i], *first_element) );
0095 if( i == j ) goto partition;
0096 std::iter_swap(array + i, array + j);
0097 }
0098 partition:
0099
0100 std::iter_swap(array + j, first_element);
0101
0102
0103
0104 i = j + 1;
0105 std::size_t new_range_size = range.size - i;
0106 range.size = j;
0107 return new_range_size;
0108 }
0109
0110 public:
0111 quick_sort_range() = default;
0112 quick_sort_range( const quick_sort_range& ) = default;
0113 void operator=( const quick_sort_range& ) = delete;
0114
0115 static constexpr std::size_t grainsize = 500;
0116 const Compare& comp;
0117 std::size_t size;
0118 RandomAccessIterator begin;
0119
0120 quick_sort_range( RandomAccessIterator begin_, std::size_t size_, const Compare& comp_ ) :
0121 comp(comp_), size(size_), begin(begin_) {}
0122
0123 bool empty() const { return size == 0; }
0124 bool is_divisible() const { return size >= grainsize; }
0125
0126 quick_sort_range( quick_sort_range& range, split )
0127 : comp(range.comp)
0128 , size(split_range(range))
0129
0130
0131 , begin(range.begin + range.size + 1) {}
0132 };
0133
0134
0135
0136 template<typename RandomAccessIterator, typename Compare>
0137 class quick_sort_pretest_body {
0138 const Compare& comp;
0139 task_group_context& context;
0140
0141 public:
0142 quick_sort_pretest_body() = default;
0143 quick_sort_pretest_body( const quick_sort_pretest_body& ) = default;
0144 void operator=( const quick_sort_pretest_body& ) = delete;
0145
0146 quick_sort_pretest_body( const Compare& _comp, task_group_context& _context ) : comp(_comp), context(_context) {}
0147
0148 void operator()( const blocked_range<RandomAccessIterator>& range ) const {
0149 RandomAccessIterator my_end = range.end();
0150
0151 int i = 0;
0152
0153 for( RandomAccessIterator k = range.begin(); k != my_end; ++k, ++i ) {
0154 if( i % 64 == 0 && context.is_group_execution_cancelled() ) break;
0155
0156
0157 if( comp(*(k), *(k - 1)) ) {
0158 context.cancel_group_execution();
0159 break;
0160 }
0161 }
0162 }
0163 };
0164
0165
0166
0167 template<typename RandomAccessIterator, typename Compare>
0168 struct quick_sort_body {
0169 void operator()( const quick_sort_range<RandomAccessIterator,Compare>& range ) const {
0170 std::sort(range.begin, range.begin + range.size, range.comp);
0171 }
0172 };
0173
0174
0175
0176 template<typename RandomAccessIterator, typename Compare>
0177 void do_parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) {
0178 parallel_for(quick_sort_range<RandomAccessIterator,Compare>(begin, end - begin, comp),
0179 quick_sort_body<RandomAccessIterator,Compare>(),
0180 auto_partitioner());
0181 }
0182
0183
0184
0185 template<typename RandomAccessIterator, typename Compare>
0186 void parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) {
0187 task_group_context my_context(PARALLEL_SORT);
0188 constexpr int serial_cutoff = 9;
0189
0190 __TBB_ASSERT( begin + serial_cutoff < end, "min_parallel_size is smaller than serial cutoff?" );
0191 RandomAccessIterator k = begin;
0192 for( ; k != begin + serial_cutoff; ++k ) {
0193 if( comp(*(k + 1), *k) ) {
0194 do_parallel_quick_sort(begin, end, comp);
0195 return;
0196 }
0197 }
0198
0199
0200 parallel_for(blocked_range<RandomAccessIterator>(k + 1, end),
0201 quick_sort_pretest_body<RandomAccessIterator, Compare>(comp, my_context),
0202 auto_partitioner(),
0203 my_context);
0204
0205 if( my_context.is_group_execution_cancelled() )
0206 do_parallel_quick_sort(begin, end, comp);
0207 }
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222 #if __TBB_CPP20_CONCEPTS_PRESENT
0223 template<typename It>
0224 using iter_value_type = typename std::iterator_traits<It>::value_type;
0225
0226 template<typename Range>
0227 using range_value_type = typename std::iterator_traits<range_iterator_type<Range>>::value_type;
0228 #endif
0229
0230
0231
0232
0233
0234 template<typename RandomAccessIterator, typename Compare>
0235 __TBB_requires(std::random_access_iterator<RandomAccessIterator> &&
0236 compare<Compare, RandomAccessIterator> &&
0237 std::movable<iter_value_type<RandomAccessIterator>>)
0238 void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) {
0239 constexpr int min_parallel_size = 500;
0240 if( end > begin ) {
0241 if( end - begin < min_parallel_size ) {
0242 std::sort(begin, end, comp);
0243 } else {
0244 parallel_quick_sort(begin, end, comp);
0245 }
0246 }
0247 }
0248
0249
0250
0251 template<typename RandomAccessIterator>
0252 __TBB_requires(std::random_access_iterator<RandomAccessIterator> &&
0253 less_than_comparable<iter_value_type<RandomAccessIterator>> &&
0254 std::movable<iter_value_type<RandomAccessIterator>>)
0255 void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end ) {
0256 parallel_sort(begin, end, std::less<typename std::iterator_traits<RandomAccessIterator>::value_type>());
0257 }
0258
0259
0260
0261 template<typename Range, typename Compare>
0262 __TBB_requires(container_based_sequence<Range, std::random_access_iterator_tag> &&
0263 compare<Compare, range_iterator_type<Range>> &&
0264 std::movable<range_value_type<Range>>)
0265 void parallel_sort( Range&& rng, const Compare& comp ) {
0266 parallel_sort(std::begin(rng), std::end(rng), comp);
0267 }
0268
0269
0270
0271 template<typename Range>
0272 __TBB_requires(container_based_sequence<Range, std::random_access_iterator_tag> &&
0273 less_than_comparable<range_value_type<Range>> &&
0274 std::movable<range_value_type<Range>>)
0275 void parallel_sort( Range&& rng ) {
0276 parallel_sort(std::begin(rng), std::end(rng));
0277 }
0278
0279
0280 }
0281 }
0282
0283 inline namespace v1 {
0284 using detail::d1::parallel_sort;
0285 }
0286 }
0287
0288 #endif