1 // -*- C++ -*- 2 3 // Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. 4 // 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 9 // version. 10 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. 15 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. 19 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/>. 24 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. 29 */ 30 31 // Written by Felix Putze and Johannes Singler. 32 33 #ifndef _GLIBCXX_PARALLEL_FIND_H 34 #define _GLIBCXX_PARALLEL_FIND_H 1 35 36 #include <bits/stl_algobase.h> 37 38 #include <parallel/features.h> 39 #include <parallel/parallel.h> 40 #include <parallel/compatibility.h> 41 #include <parallel/equally_split.h> 42 43 namespace __gnu_parallel 44 { 45 /** 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. 54 */ 55 template<typename RandomAccessIterator1, 56 typename RandomAccessIterator2, 57 typename Pred, 58 typename Selector> 59 inline std::pair<RandomAccessIterator1, RandomAccessIterator2> 60 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1, 61 RandomAccessIterator2 begin2, Pred pred, Selector selector) 62 { 63 switch (_Settings::get().find_algorithm) 64 { 65 case GROWING_BLOCKS: 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()); 71 case EQUAL_SPLIT: 72 return find_template(begin1, end1, begin2, pred, selector, 73 equal_split_tag()); 74 default: 75 _GLIBCXX_PARALLEL_ASSERT(false); 76 return std::make_pair(begin1, begin2); 77 } 78 } 79 80 #if _GLIBCXX_FIND_EQUAL_SPLIT 81 82 /** 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. 91 */ 92 template<typename RandomAccessIterator1, 93 typename RandomAccessIterator2, 94 typename Pred, 95 typename Selector> 96 std::pair<RandomAccessIterator1, RandomAccessIterator2> 97 find_template(RandomAccessIterator1 begin1, 98 RandomAccessIterator1 end1, 99 RandomAccessIterator2 begin2, 100 Pred pred, 101 Selector selector, 102 equal_split_tag) 103 { 104 _GLIBCXX_CALL(end1 - begin1) 105 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; 109 110 difference_type length = end1 - begin1; 111 difference_type result = length; 112 difference_type* borders; 113 114 omp_lock_t result_lock; 115 omp_init_lock(&result_lock); 116 117 thread_index_t num_threads = get_max_threads(); 118 # pragma omp parallel num_threads(num_threads) 119 { 120 # pragma omp single 121 { 122 num_threads = omp_get_num_threads(); 123 borders = new difference_type[num_threads + 1]; 124 equally_split(length, num_threads, borders); 125 } //single 126 127 thread_index_t iam = omp_get_thread_num(); 128 difference_type start = borders[iam], stop = borders[iam + 1]; 129 130 RandomAccessIterator1 i1 = begin1 + start; 131 RandomAccessIterator2 i2 = begin2 + start; 132 for (difference_type pos = start; pos < stop; ++pos) 133 { 134 #pragma omp flush(result) 135 // Result has been set to something lower. 136 if (result < pos) 137 break; 138 139 if (selector(i1, i2, pred)) 140 { 141 omp_set_lock(&result_lock); 142 if (pos < result) 143 result = pos; 144 omp_unset_lock(&result_lock); 145 break; 146 } 147 ++i1; 148 ++i2; 149 } 150 } //parallel 151 152 omp_destroy_lock(&result_lock); 153 delete[] borders; 154 155 return 156 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result, 157 begin2 + result); 158 } 159 160 #endif 161 162 #if _GLIBCXX_FIND_GROWING_BLOCKS 163 164 /** 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 177 * 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. 181 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. 185 */ 186 template<typename RandomAccessIterator1, 187 typename RandomAccessIterator2, 188 typename Pred, 189 typename Selector> 190 std::pair<RandomAccessIterator1, RandomAccessIterator2> 191 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1, 192 RandomAccessIterator2 begin2, Pred pred, Selector selector, 193 growing_blocks_tag) 194 { 195 _GLIBCXX_CALL(end1 - begin1) 196 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; 200 201 const _Settings& __s = _Settings::get(); 202 203 difference_type length = end1 - begin1; 204 205 difference_type sequential_search_size = 206 std::min<difference_type>(length, __s.find_sequential_search_size); 207 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); 212 213 if (find_seq_result.first != (begin1 + sequential_search_size)) 214 return find_seq_result; 215 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; 219 220 omp_lock_t result_lock; 221 omp_init_lock(&result_lock); 222 223 thread_index_t num_threads = get_max_threads(); 224 # pragma omp parallel shared(result) num_threads(num_threads) 225 { 226 # pragma omp single 227 num_threads = omp_get_num_threads(); 228 229 // Not within first k elements -> start parallel. 230 thread_index_t iam = omp_get_thread_num(); 231 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); 235 236 // Get new block, update pointer to next block. 237 difference_type stop = 238 std::min<difference_type>(length, start + block_size); 239 240 std::pair<RandomAccessIterator1, RandomAccessIterator2> local_result; 241 242 while (start < length) 243 { 244 # pragma omp flush(result) 245 // Get new value of result. 246 if (result < start) 247 { 248 // No chance to find first element. 249 break; 250 } 251 252 local_result = selector.sequential_algorithm( 253 begin1 + start, begin1 + stop, begin2 + start, pred); 254 if (local_result.first != (begin1 + stop)) 255 { 256 omp_set_lock(&result_lock); 257 if ((local_result.first - begin1) < result) 258 { 259 result = local_result.first - begin1; 260 261 // Result cannot be in future blocks, stop algorithm. 262 fetch_and_add<difference_type>(&next_block_start, length); 263 } 264 omp_unset_lock(&result_lock); 265 } 266 267 block_size = 268 std::min<difference_type>(block_size * __s.find_increasing_factor, 269 __s.find_maximum_block_size); 270 271 // Get new block, update pointer to next block. 272 start = 273 fetch_and_add<difference_type>(&next_block_start, block_size); 274 stop = ((length < (start + block_size)) 275 ? length : (start + block_size)); 276 } 277 } //parallel 278 279 omp_destroy_lock(&result_lock); 280 281 // Return iterator on found element. 282 return 283 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result, 284 begin2 + result); 285 } 286 287 #endif 288 289 #if _GLIBCXX_FIND_CONSTANT_SIZE_BLOCKS 290 291 /** 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 307 * round-robin. 308 */ 309 template<typename RandomAccessIterator1, 310 typename RandomAccessIterator2, 311 typename Pred, 312 typename Selector> 313 std::pair<RandomAccessIterator1, RandomAccessIterator2> 314 find_template(RandomAccessIterator1 begin1, RandomAccessIterator1 end1, 315 RandomAccessIterator2 begin2, Pred pred, Selector selector, 316 constant_size_blocks_tag) 317 { 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; 322 323 const _Settings& __s = _Settings::get(); 324 325 difference_type length = end1 - begin1; 326 327 difference_type sequential_search_size = std::min<difference_type>( 328 length, __s.find_sequential_search_size); 329 330 // Try it sequentially first. 331 std::pair<RandomAccessIterator1, RandomAccessIterator2> find_seq_result = 332 selector.sequential_algorithm(begin1, begin1 + sequential_search_size, 333 begin2, pred); 334 335 if (find_seq_result.first != (begin1 + sequential_search_size)) 336 return find_seq_result; 337 338 difference_type result = length; 339 omp_lock_t result_lock; 340 omp_init_lock(&result_lock); 341 342 // Not within first sequential_search_size elements -> start parallel. 343 344 thread_index_t num_threads = get_max_threads(); 345 # pragma omp parallel shared(result) num_threads(num_threads) 346 { 347 # pragma omp single 348 num_threads = omp_get_num_threads(); 349 350 thread_index_t iam = omp_get_thread_num(); 351 difference_type block_size = __s.find_initial_block_size; 352 353 // First element of thread's current iteration. 354 difference_type iteration_start = sequential_search_size; 355 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); 360 361 std::pair<RandomAccessIterator1, RandomAccessIterator2> local_result; 362 363 while (start < length) 364 { 365 // Get new value of result. 366 # pragma omp flush(result) 367 // No chance to find first element. 368 if (result < start) 369 break; 370 local_result = selector.sequential_algorithm( 371 begin1 + start, begin1 + stop, 372 begin2 + start, pred); 373 if (local_result.first != (begin1 + stop)) 374 { 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. 380 break; 381 } 382 383 iteration_start += num_threads * block_size; 384 385 // Where to work. 386 start = iteration_start + iam * block_size; 387 stop = std::min<difference_type>(length, start + block_size); 388 } 389 } //parallel 390 391 omp_destroy_lock(&result_lock); 392 393 // Return iterator on found element. 394 return 395 std::pair<RandomAccessIterator1, RandomAccessIterator2>(begin1 + result, 396 begin2 + result); 397 } 398 #endif 399 } // end namespace 400 401 #endif /* _GLIBCXX_PARALLEL_FIND_H */ 402