]> git.sesse.net Git - casparcg/blob - tbb/include/tbb/parallel_sort.h
2.0. Updated tbb library.
[casparcg] / tbb / include / tbb / parallel_sort.h
1 /*
2     Copyright 2005-2011 Intel Corporation.  All Rights Reserved.
3
4     This file is part of Threading Building Blocks.
5
6     Threading Building Blocks is free software; you can redistribute it
7     and/or modify it under the terms of the GNU General Public License
8     version 2 as published by the Free Software Foundation.
9
10     Threading Building Blocks is distributed in the hope that it will be
11     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with Threading Building Blocks; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     As a special exception, you may use this file as part of a free software
20     library without restriction.  Specifically, if other files instantiate
21     templates or use macros or inline functions from this file, or you compile
22     this file and link it with other files to produce an executable, this
23     file does not by itself cause the resulting executable to be covered by
24     the GNU General Public License.  This exception does not however
25     invalidate any other reasons why the executable file might be covered by
26     the GNU General Public License.
27 */
28
29 #ifndef __TBB_parallel_sort_H
30 #define __TBB_parallel_sort_H
31
32 #include "parallel_for.h"
33 #include "blocked_range.h"
34 #include <algorithm>
35 #include <iterator>
36 #include <functional>
37
38 namespace tbb {
39
40 //! @cond INTERNAL
41 namespace internal {
42
43 //! Range used in quicksort to split elements into subranges based on a value.
44 /** The split operation selects a splitter and places all elements less than or equal 
45     to the value in the first range and the remaining elements in the second range.
46     @ingroup algorithms */
47 template<typename RandomAccessIterator, typename Compare>
48 class quick_sort_range: private no_assign {
49
50     inline size_t median_of_three(const RandomAccessIterator &array, size_t l, size_t m, size_t r) const {
51         return comp(array[l], array[m]) ? ( comp(array[m], array[r]) ? m : ( comp( array[l], array[r]) ? r : l ) ) 
52                                         : ( comp(array[r], array[m]) ? m : ( comp( array[r], array[l] ) ? r : l ) );
53     }
54
55     inline size_t pseudo_median_of_nine( const RandomAccessIterator &array, const quick_sort_range &range ) const {
56         size_t offset = range.size/8u;
57         return median_of_three(array, 
58                                median_of_three(array, 0, offset, offset*2),
59                                median_of_three(array, offset*3, offset*4, offset*5),
60                                median_of_three(array, offset*6, offset*7, range.size - 1) );
61
62     }
63
64 public:
65
66     static const size_t grainsize = 500;
67     const Compare &comp;
68     RandomAccessIterator begin;
69     size_t size;
70
71     quick_sort_range( RandomAccessIterator begin_, size_t size_, const Compare &comp_ ) :
72         comp(comp_), begin(begin_), size(size_) {}
73
74     bool empty() const {return size==0;}
75     bool is_divisible() const {return size>=grainsize;}
76
77     quick_sort_range( quick_sort_range& range, split ) : comp(range.comp) {
78         RandomAccessIterator array = range.begin;
79         RandomAccessIterator key0 = range.begin; 
80         size_t m = pseudo_median_of_nine(array, range);
81         if (m) std::swap ( array[0], array[m] );
82
83         size_t i=0;
84         size_t j=range.size;
85         // Partition interval [i+1,j-1] with key *key0.
86         for(;;) {
87             __TBB_ASSERT( i<j, NULL );
88             // Loop must terminate since array[l]==*key0.
89             do {
90                 --j;
91                 __TBB_ASSERT( i<=j, "bad ordering relation?" );
92             } while( comp( *key0, array[j] ));
93             do {
94                 __TBB_ASSERT( i<=j, NULL );
95                 if( i==j ) goto partition;
96                 ++i;
97             } while( comp( array[i],*key0 ));
98             if( i==j ) goto partition;
99             std::swap( array[i], array[j] );
100         }
101 partition:
102         // Put the partition key were it belongs
103         std::swap( array[j], *key0 );
104         // array[l..j) is less or equal to key.
105         // array(j..r) is greater or equal to key.
106         // array[j] is equal to key
107         i=j+1;
108         begin = array+i;
109         size = range.size-i;
110         range.size = j;
111     }
112 };
113
114 #if __TBB_TASK_GROUP_CONTEXT
115 //! Body class used to test if elements in a range are presorted
116 /** @ingroup algorithms */
117 template<typename RandomAccessIterator, typename Compare>
118 class quick_sort_pretest_body : internal::no_assign {
119     const Compare &comp;
120
121 public:
122     quick_sort_pretest_body(const Compare &_comp) : comp(_comp) {}
123
124     void operator()( const blocked_range<RandomAccessIterator>& range ) const {
125         task &my_task = task::self();
126         RandomAccessIterator my_end = range.end();
127
128         int i = 0;
129         for (RandomAccessIterator k = range.begin(); k != my_end; ++k, ++i) {
130             if ( i%64 == 0 && my_task.is_cancelled() ) break;
131           
132             // The k-1 is never out-of-range because the first chunk starts at begin+serial_cutoff+1
133             if ( comp( *(k), *(k-1) ) ) {
134                 my_task.cancel_group_execution();
135                 break;
136             }
137         }
138     }
139
140 };
141 #endif /* __TBB_TASK_GROUP_CONTEXT */
142
143 //! Body class used to sort elements in a range that is smaller than the grainsize.
144 /** @ingroup algorithms */
145 template<typename RandomAccessIterator, typename Compare>
146 struct quick_sort_body {
147     void operator()( const quick_sort_range<RandomAccessIterator,Compare>& range ) const {
148         //SerialQuickSort( range.begin, range.size, range.comp );
149         std::sort( range.begin, range.begin + range.size, range.comp );
150     }
151 };
152
153 //! Wrapper method to initiate the sort by calling parallel_for.
154 /** @ingroup algorithms */
155 template<typename RandomAccessIterator, typename Compare>
156 void parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) {
157 #if __TBB_TASK_GROUP_CONTEXT
158     task_group_context my_context;
159     const int serial_cutoff = 9;
160
161     __TBB_ASSERT( begin + serial_cutoff < end, "min_parallel_size is smaller than serial cutoff?" );
162     RandomAccessIterator k;
163     for ( k = begin ; k != begin + serial_cutoff; ++k ) {
164         if ( comp( *(k+1), *k ) ) {
165             goto do_parallel_quick_sort;
166         }
167     }
168
169     parallel_for( blocked_range<RandomAccessIterator>(k+1, end),
170                   quick_sort_pretest_body<RandomAccessIterator,Compare>(comp),
171                   auto_partitioner(),
172                   my_context);
173
174     if (my_context.is_group_execution_cancelled())
175 do_parallel_quick_sort:
176 #endif /* __TBB_TASK_GROUP_CONTEXT */
177         parallel_for( quick_sort_range<RandomAccessIterator,Compare>(begin, end-begin, comp ), 
178                       quick_sort_body<RandomAccessIterator,Compare>(),
179                       auto_partitioner() );
180 }
181
182 } // namespace internal
183 //! @endcond
184
185 /** \page parallel_sort_iter_req Requirements on iterators for parallel_sort
186     Requirements on value type \c T of \c RandomAccessIterator for \c parallel_sort:
187     - \code void swap( T& x, T& y ) \endcode        Swaps \c x and \c y
188     - \code bool Compare::operator()( const T& x, const T& y ) \endcode
189                                                     True if x comes before y;
190 **/
191
192 /** \name parallel_sort
193     See also requirements on \ref parallel_sort_iter_req "iterators for parallel_sort". **/
194 //@{
195
196 //! Sorts the data in [begin,end) using the given comparator 
197 /** The compare function object is used for all comparisons between elements during sorting.
198     The compare object must define a bool operator() function.
199     @ingroup algorithms **/
200 template<typename RandomAccessIterator, typename Compare>
201 void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp) { 
202     const int min_parallel_size = 500; 
203     if( end > begin ) {
204         if (end - begin < min_parallel_size) { 
205             std::sort(begin, end, comp);
206         } else {
207             internal::parallel_quick_sort(begin, end, comp);
208         }
209     }
210 }
211
212 //! Sorts the data in [begin,end) with a default comparator \c std::less<RandomAccessIterator>
213 /** @ingroup algorithms **/
214 template<typename RandomAccessIterator>
215 inline void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end ) { 
216     parallel_sort( begin, end, std::less< typename std::iterator_traits<RandomAccessIterator>::value_type >() );
217 }
218
219 //! Sorts the data in the range \c [begin,end) with a default comparator \c std::less<T>
220 /** @ingroup algorithms **/
221 template<typename T>
222 inline void parallel_sort( T * begin, T * end ) {
223     parallel_sort( begin, end, std::less< T >() );
224 }   
225 //@}
226
227
228 } // namespace tbb
229
230 #endif
231