]> git.sesse.net Git - casparcg/blob - tbb30_20100406oss/include/tbb/parallel_sort.h
6fbbe8073c37d56fdb73e29937439385fa279260
[casparcg] / tbb30_20100406oss / include / tbb / parallel_sort.h
1 /*
2     Copyright 2005-2010 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 //! Body class used to test if elements in a range are presorted
115 /** @ingroup algorithms */
116 template<typename RandomAccessIterator, typename Compare>
117 class quick_sort_pretest_body : internal::no_assign {
118     const Compare &comp;
119
120 public:
121     quick_sort_pretest_body(const Compare &_comp) : comp(_comp) {}
122
123     void operator()( const blocked_range<RandomAccessIterator>& range ) const {
124         task &my_task = task::self();
125         RandomAccessIterator my_end = range.end();
126
127         int i = 0;
128         for (RandomAccessIterator k = range.begin(); k != my_end; ++k, ++i) {
129             if ( i%64 == 0 && my_task.is_cancelled() ) break;
130           
131             // The k-1 is never out-of-range because the first chunk starts at begin+serial_cutoff+1
132             if ( comp( *(k), *(k-1) ) ) {
133                 my_task.cancel_group_execution();
134                 break;
135             }
136         }
137     }
138
139 };
140
141 //! Body class used to sort elements in a range that is smaller than the grainsize.
142 /** @ingroup algorithms */
143 template<typename RandomAccessIterator, typename Compare>
144 struct quick_sort_body {
145     void operator()( const quick_sort_range<RandomAccessIterator,Compare>& range ) const {
146         //SerialQuickSort( range.begin, range.size, range.comp );
147         std::sort( range.begin, range.begin + range.size, range.comp );
148     }
149 };
150
151 //! Wrapper method to initiate the sort by calling parallel_for.
152 /** @ingroup algorithms */
153 template<typename RandomAccessIterator, typename Compare>
154 void parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) {
155     task_group_context my_context;
156     const int serial_cutoff = 9;
157
158     __TBB_ASSERT( begin + serial_cutoff < end, "min_parallel_size is smaller than serial cutoff?" );
159     RandomAccessIterator k;
160     for ( k = begin ; k != begin + serial_cutoff; ++k ) {
161         if ( comp( *(k+1), *k ) ) {
162             goto do_parallel_quick_sort;
163         }
164     }
165
166     parallel_for( blocked_range<RandomAccessIterator>(k+1, end),
167                   quick_sort_pretest_body<RandomAccessIterator,Compare>(comp),
168                   auto_partitioner(),
169                   my_context);
170
171     if (my_context.is_group_execution_cancelled())
172 do_parallel_quick_sort:
173         parallel_for( quick_sort_range<RandomAccessIterator,Compare>(begin, end-begin, comp ), 
174                       quick_sort_body<RandomAccessIterator,Compare>(),
175                       auto_partitioner() );
176 }
177
178 } // namespace internal
179 //! @endcond
180
181 /** \page parallel_sort_iter_req Requirements on iterators for parallel_sort
182     Requirements on value type \c T of \c RandomAccessIterator for \c parallel_sort:
183     - \code void swap( T& x, T& y ) \endcode        Swaps \c x and \c y
184     - \code bool Compare::operator()( const T& x, const T& y ) \endcode
185                                                     True if x comes before y;
186 **/
187
188 /** \name parallel_sort
189     See also requirements on \ref parallel_sort_iter_req "iterators for parallel_sort". **/
190 //@{
191
192 //! Sorts the data in [begin,end) using the given comparator 
193 /** The compare function object is used for all comparisons between elements during sorting.
194     The compare object must define a bool operator() function.
195     @ingroup algorithms **/
196 template<typename RandomAccessIterator, typename Compare>
197 void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp) { 
198     const int min_parallel_size = 500; 
199     if( end > begin ) {
200         if (end - begin < min_parallel_size) { 
201             std::sort(begin, end, comp);
202         } else {
203             internal::parallel_quick_sort(begin, end, comp);
204         }
205     }
206 }
207
208 //! Sorts the data in [begin,end) with a default comparator \c std::less<RandomAccessIterator>
209 /** @ingroup algorithms **/
210 template<typename RandomAccessIterator>
211 inline void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end ) { 
212     parallel_sort( begin, end, std::less< typename std::iterator_traits<RandomAccessIterator>::value_type >() );
213 }
214
215 //! Sorts the data in the range \c [begin,end) with a default comparator \c std::less<T>
216 /** @ingroup algorithms **/
217 template<typename T>
218 inline void parallel_sort( T * begin, T * end ) {
219     parallel_sort( begin, end, std::less< T >() );
220 }   
221 //@}
222
223
224 } // namespace tbb
225
226 #endif
227