3 // Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the terms
7 // of the GNU General Public License as published by the Free Software
8 // Foundation; either version 3, or (at your option) any later
11 // This library is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
25 /** @file parallel/find.h
26 * @brief Parallel implementation base for std::find(), std::equal()
27 * and related functions.
28 * This file is a GNU parallel extension to the Standard C++ Library.
31 // Written by Felix Putze and Johannes Singler.
33 #ifndef _GLIBCXX_PARALLEL_FIND_H
34 #define _GLIBCXX_PARALLEL_FIND_H 1
36 #include <bits/stl_algobase.h>
38 #include <parallel/features.h>
39 #include <parallel/parallel.h>
40 #include <parallel/compatibility.h>
41 #include <parallel/equally_split.h>
43 namespace __gnu_parallel
46 * @brief Parallel std::find, switch for different algorithms.
47 * @param begin1 Begin iterator of first sequence.
48 * @param end1 End iterator of first sequence.
49 * @param begin2 Begin iterator of second sequence. Must have same
50 * length as first sequence.
51 * @param pred Find predicate.
52 * @param selector Functionality (e. g. std::find_if (), std::equal(),...)
53 * @return Place of finding in both sequences.
55 template<typename RandomAccessIterator1,
56 typename RandomAccessIterator2,
59 inline std::pair<RandomAccessIterator1, RandomAccessIterator2>
60 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
61 RandomAccessIterator2 begin2, Pred pred, Selector selector)
63 switch (_Settings::get().find_algorithm)
66 return find_template(begin1, end1, begin2, pred, selector,
67 growing_blocks_tag());
68 case CONSTANT_SIZE_BLOCKS:
69 return find_template(begin1, end1, begin2, pred, selector,
70 constant_size_blocks_tag());
72 return find_template(begin1, end1, begin2, pred, selector,
75 _GLIBCXX_PARALLEL_ASSERT(false);
76 return std::make_pair(begin1, begin2);
80 #if _GLIBCXX_FIND_EQUAL_SPLIT
83 * @brief Parallel std::find, equal splitting variant.
84 * @param begin1 Begin iterator of first sequence.
85 * @param end1 End iterator of first sequence.
86 * @param begin2 Begin iterator of second sequence. Second sequence
87 * must have same length as first sequence.
88 * @param pred Find predicate.
89 * @param selector Functionality (e. g. std::find_if (), std::equal(),...)
90 * @return Place of finding in both sequences.
92 template<typename RandomAccessIterator1,
93 typename RandomAccessIterator2,
96 std::pair<RandomAccessIterator1, RandomAccessIterator2>
97 find_template(RandomAccessIterator1 begin1,
98 RandomAccessIterator1 end1,
99 RandomAccessIterator2 begin2,
104 _GLIBCXX_CALL(end1 - begin1)
106 typedef std::iterator_traits<RandomAccessIterator1> traits_type;
107 typedef typename traits_type::difference_type difference_type;
108 typedef typename traits_type::value_type value_type;
110 difference_type length = end1 - begin1;
111 difference_type result = length;
112 difference_type* borders;
114 omp_lock_t result_lock;
115 omp_init_lock(&result_lock);
117 thread_index_t num_threads = get_max_threads();
118 # pragma omp parallel num_threads(num_threads)
122 num_threads = omp_get_num_threads();
123 borders = new difference_type[num_threads + 1];
124 equally_split(length, num_threads, borders);
127 thread_index_t iam = omp_get_thread_num();
128 difference_type start = borders[iam], stop = borders[iam + 1];
130 RandomAccessIterator1 i1 = begin1 + start;
131 RandomAccessIterator2 i2 = begin2 + start;
132 for (difference_type pos = start; pos < stop; ++pos)
134 #pragma omp flush(result)
135 // Result has been set to something lower.
139 if (selector(i1, i2, pred))
141 omp_set_lock(&result_lock);
144 omp_unset_lock(&result_lock);
152 omp_destroy_lock(&result_lock);
156 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result,
162 #if _GLIBCXX_FIND_GROWING_BLOCKS
165 * @brief Parallel std::find, growing block size variant.
166 * @param begin1 Begin iterator of first sequence.
167 * @param end1 End iterator of first sequence.
168 * @param begin2 Begin iterator of second sequence. Second sequence
169 * must have same length as first sequence.
170 * @param pred Find predicate.
171 * @param selector Functionality (e. g. std::find_if (), std::equal(),...)
172 * @return Place of finding in both sequences.
173 * @see __gnu_parallel::_Settings::find_sequential_search_size
174 * @see __gnu_parallel::_Settings::find_initial_block_size
175 * @see __gnu_parallel::_Settings::find_maximum_block_size
176 * @see __gnu_parallel::_Settings::find_increasing_factor
178 * There are two main differences between the growing blocks and
179 * the constant-size blocks variants.
180 * 1. For GB, the block size grows; for CSB, the block size is fixed.
182 * 2. For GB, the blocks are allocated dynamically;
183 * for CSB, the blocks are allocated in a predetermined manner,
184 * namely spacial round-robin.
186 template<typename RandomAccessIterator1,
187 typename RandomAccessIterator2,
190 std::pair<RandomAccessIterator1, RandomAccessIterator2>
191 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
192 RandomAccessIterator2 begin2, Pred pred, Selector selector,
195 _GLIBCXX_CALL(end1 - begin1)
197 typedef std::iterator_traits<RandomAccessIterator1> traits_type;
198 typedef typename traits_type::difference_type difference_type;
199 typedef typename traits_type::value_type value_type;
201 const _Settings& __s = _Settings::get();
203 difference_type length = end1 - begin1;
205 difference_type sequential_search_size =
206 std::min<difference_type>(length, __s.find_sequential_search_size);
208 // Try it sequentially first.
209 std::pair<RandomAccessIterator1, RandomAccessIterator2> find_seq_result =
210 selector.sequential_algorithm(
211 begin1, begin1 + sequential_search_size, begin2, pred);
213 if (find_seq_result.first != (begin1 + sequential_search_size))
214 return find_seq_result;
216 // Index of beginning of next free block (after sequential find).
217 difference_type next_block_start = sequential_search_size;
218 difference_type result = length;
220 omp_lock_t result_lock;
221 omp_init_lock(&result_lock);
223 thread_index_t num_threads = get_max_threads();
224 # pragma omp parallel shared(result) num_threads(num_threads)
227 num_threads = omp_get_num_threads();
229 // Not within first k elements -> start parallel.
230 thread_index_t iam = omp_get_thread_num();
232 difference_type block_size = __s.find_initial_block_size;
233 difference_type start =
234 fetch_and_add<difference_type>(&next_block_start, block_size);
236 // Get new block, update pointer to next block.
237 difference_type stop =
238 std::min<difference_type>(length, start + block_size);
240 std::pair<RandomAccessIterator1, RandomAccessIterator2> local_result;
242 while (start < length)
244 # pragma omp flush(result)
245 // Get new value of result.
248 // No chance to find first element.
252 local_result = selector.sequential_algorithm(
253 begin1 + start, begin1 + stop, begin2 + start, pred);
254 if (local_result.first != (begin1 + stop))
256 omp_set_lock(&result_lock);
257 if ((local_result.first - begin1) < result)
259 result = local_result.first - begin1;
261 // Result cannot be in future blocks, stop algorithm.
262 fetch_and_add<difference_type>(&next_block_start, length);
264 omp_unset_lock(&result_lock);
268 std::min<difference_type>(block_size * __s.find_increasing_factor,
269 __s.find_maximum_block_size);
271 // Get new block, update pointer to next block.
273 fetch_and_add<difference_type>(&next_block_start, block_size);
274 stop = ((length < (start + block_size))
275 ? length : (start + block_size));
279 omp_destroy_lock(&result_lock);
281 // Return iterator on found element.
283 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result,
289 #if _GLIBCXX_FIND_CONSTANT_SIZE_BLOCKS
292 * @brief Parallel std::find, constant block size variant.
293 * @param begin1 Begin iterator of first sequence.
294 * @param end1 End iterator of first sequence.
295 * @param begin2 Begin iterator of second sequence. Second sequence
296 * must have same length as first sequence.
297 * @param pred Find predicate.
298 * @param selector Functionality (e. g. std::find_if (), std::equal(),...)
299 * @return Place of finding in both sequences.
300 * @see __gnu_parallel::_Settings::find_sequential_search_size
301 * @see __gnu_parallel::_Settings::find_block_size
302 * There are two main differences between the growing blocks and the
303 * constant-size blocks variants.
304 * 1. For GB, the block size grows; for CSB, the block size is fixed.
305 * 2. For GB, the blocks are allocated dynamically; for CSB, the
306 * blocks are allocated in a predetermined manner, namely spacial
309 template<typename RandomAccessIterator1,
310 typename RandomAccessIterator2,
313 std::pair<RandomAccessIterator1, RandomAccessIterator2>
314 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
315 RandomAccessIterator2 begin2, Pred pred, Selector selector,
316 constant_size_blocks_tag)
318 _GLIBCXX_CALL(end1 - begin1)
319 typedef std::iterator_traits<RandomAccessIterator1> traits_type;
320 typedef typename traits_type::difference_type difference_type;
321 typedef typename traits_type::value_type value_type;
323 const _Settings& __s = _Settings::get();
325 difference_type length = end1 - begin1;
327 difference_type sequential_search_size = std::min<difference_type>(
328 length, __s.find_sequential_search_size);
330 // Try it sequentially first.
331 std::pair<RandomAccessIterator1, RandomAccessIterator2> find_seq_result =
332 selector.sequential_algorithm(begin1, begin1 + sequential_search_size,
335 if (find_seq_result.first != (begin1 + sequential_search_size))
336 return find_seq_result;
338 difference_type result = length;
339 omp_lock_t result_lock;
340 omp_init_lock(&result_lock);
342 // Not within first sequential_search_size elements -> start parallel.
344 thread_index_t num_threads = get_max_threads();
345 # pragma omp parallel shared(result) num_threads(num_threads)
348 num_threads = omp_get_num_threads();
350 thread_index_t iam = omp_get_thread_num();
351 difference_type block_size = __s.find_initial_block_size;
353 // First element of thread's current iteration.
354 difference_type iteration_start = sequential_search_size;
356 // Where to work (initialization).
357 difference_type start = iteration_start + iam * block_size;
358 difference_type stop =
359 std::min<difference_type>(length, start + block_size);
361 std::pair<RandomAccessIterator1, RandomAccessIterator2> local_result;
363 while (start < length)
365 // Get new value of result.
366 # pragma omp flush(result)
367 // No chance to find first element.
370 local_result = selector.sequential_algorithm(
371 begin1 + start, begin1 + stop,
372 begin2 + start, pred);
373 if (local_result.first != (begin1 + stop))
375 omp_set_lock(&result_lock);
376 if ((local_result.first - begin1) < result)
377 result = local_result.first - begin1;
378 omp_unset_lock(&result_lock);
379 // Will not find better value in its interval.
383 iteration_start += num_threads * block_size;
386 start = iteration_start + iam * block_size;
387 stop = std::min<difference_type>(length, start + block_size);
391 omp_destroy_lock(&result_lock);
393 // Return iterator on found element.
395 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result,
401 #endif /* _GLIBCXX_PARALLEL_FIND_H */