| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- // -*- C++ -*-
- // Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
- //
- // This file is part of the GNU ISO C++ Library. This library is free
- // software; you can redistribute it and/or modify it under the terms
- // of the GNU General Public License as published by the Free Software
- // Foundation; either version 3, or (at your option) any later
- // version.
- // This library is distributed in the hope that it will be useful, but
- // WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // General Public License for more details.
- // Under Section 7 of GPL version 3, you are granted additional
- // permissions described in the GCC Runtime Library Exception, version
- // 3.1, as published by the Free Software Foundation.
- // You should have received a copy of the GNU General Public License and
- // a copy of the GCC Runtime Library Exception along with this program;
- // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- // <http://www.gnu.org/licenses/>.
- /** @file parallel/partition.h
- * @brief Parallel implementation of std::partition(),
- * std::nth_element(), and std::partial_sort().
- * This file is a GNU parallel extension to the Standard C++ Library.
- */
- // Written by Johannes Singler and Felix Putze.
- #ifndef _GLIBCXX_PARALLEL_PARTITION_H
- #define _GLIBCXX_PARALLEL_PARTITION_H 1
- #include <parallel/basic_iterator.h>
- #include <parallel/sort.h>
- #include <parallel/random_number.h>
- #include <bits/stl_algo.h>
- #include <parallel/parallel.h>
- /** @brief Decide whether to declare certain variables volatile. */
- #define _GLIBCXX_VOLATILE volatile
- namespace __gnu_parallel
- {
- /** @brief Parallel implementation of std::partition.
- * @param begin Begin iterator of input sequence to split.
- * @param end End iterator of input sequence to split.
- * @param pred Partition predicate, possibly including some kind of pivot.
- * @param num_threads Maximum number of threads to use for this task.
- * @return Number of elements not fulfilling the predicate. */
- template<typename RandomAccessIterator, typename Predicate>
- typename std::iterator_traits<RandomAccessIterator>::difference_type
- parallel_partition(RandomAccessIterator begin, RandomAccessIterator end,
- Predicate pred, thread_index_t num_threads)
- {
- typedef std::iterator_traits<RandomAccessIterator> traits_type;
- typedef typename traits_type::value_type value_type;
- typedef typename traits_type::difference_type difference_type;
- difference_type n = end - begin;
- _GLIBCXX_CALL(n)
- const _Settings& __s = _Settings::get();
- // Shared.
- _GLIBCXX_VOLATILE difference_type left = 0, right = n - 1;
- _GLIBCXX_VOLATILE difference_type leftover_left, leftover_right;
- _GLIBCXX_VOLATILE difference_type leftnew, rightnew;
- bool* reserved_left = NULL, * reserved_right = NULL;
- difference_type chunk_size = __s.partition_chunk_size;
- omp_lock_t result_lock;
- omp_init_lock(&result_lock);
- //at least two chunks per thread
- if(right - left + 1 >= 2 * num_threads * chunk_size)
- # pragma omp parallel num_threads(num_threads)
- {
- # pragma omp single
- {
- num_threads = omp_get_num_threads();
- reserved_left = new bool[num_threads];
- reserved_right = new bool[num_threads];
- if (__s.partition_chunk_share > 0.0)
- chunk_size = std::max<difference_type>(__s.partition_chunk_size,
- (double)n * __s.partition_chunk_share
- / (double)num_threads);
- else
- chunk_size = __s.partition_chunk_size;
- }
- while (right - left + 1 >= 2 * num_threads * chunk_size)
- {
- # pragma omp single
- {
- difference_type num_chunks = (right - left + 1) / chunk_size;
- for (int r = 0; r < num_threads; ++r)
- {
- reserved_left[r] = false;
- reserved_right[r] = false;
- }
- leftover_left = 0;
- leftover_right = 0;
- } //implicit barrier
- // Private.
- difference_type thread_left, thread_left_border,
- thread_right, thread_right_border;
- thread_left = left + 1;
- // Just to satisfy the condition below.
- thread_left_border = thread_left - 1;
- thread_right = n - 1;
- thread_right_border = thread_right + 1;
- bool iam_finished = false;
- while (!iam_finished)
- {
- if (thread_left > thread_left_border)
- {
- omp_set_lock(&result_lock);
- if (left + (chunk_size - 1) > right)
- iam_finished = true;
- else
- {
- thread_left = left;
- thread_left_border = left + (chunk_size - 1);
- left += chunk_size;
- }
- omp_unset_lock(&result_lock);
- }
- if (thread_right < thread_right_border)
- {
- omp_set_lock(&result_lock);
- if (left > right - (chunk_size - 1))
- iam_finished = true;
- else
- {
- thread_right = right;
- thread_right_border = right - (chunk_size - 1);
- right -= chunk_size;
- }
- omp_unset_lock(&result_lock);
- }
- if (iam_finished)
- break;
- // Swap as usual.
- while (thread_left < thread_right)
- {
- while (pred(begin[thread_left])
- && thread_left <= thread_left_border)
- ++thread_left;
- while (!pred(begin[thread_right])
- && thread_right >= thread_right_border)
- --thread_right;
- if (thread_left > thread_left_border
- || thread_right < thread_right_border)
- // Fetch new chunk(s).
- break;
- std::swap(begin[thread_left], begin[thread_right]);
- ++thread_left;
- --thread_right;
- }
- }
- // Now swap the leftover chunks to the right places.
- if (thread_left <= thread_left_border)
- # pragma omp atomic
- ++leftover_left;
- if (thread_right >= thread_right_border)
- # pragma omp atomic
- ++leftover_right;
- # pragma omp barrier
- # pragma omp single
- {
- leftnew = left - leftover_left * chunk_size;
- rightnew = right + leftover_right * chunk_size;
- }
- # pragma omp barrier
- // <=> thread_left_border + (chunk_size - 1) >= leftnew
- if (thread_left <= thread_left_border
- && thread_left_border >= leftnew)
- {
- // Chunk already in place, reserve spot.
- reserved_left[(left - (thread_left_border + 1)) / chunk_size]
- = true;
- }
- // <=> thread_right_border - (chunk_size - 1) <= rightnew
- if (thread_right >= thread_right_border
- && thread_right_border <= rightnew)
- {
- // Chunk already in place, reserve spot.
- reserved_right[((thread_right_border - 1) - right)
- / chunk_size] = true;
- }
- # pragma omp barrier
- if (thread_left <= thread_left_border
- && thread_left_border < leftnew)
- {
- // Find spot and swap.
- difference_type swapstart = -1;
- omp_set_lock(&result_lock);
- for (int r = 0; r < leftover_left; ++r)
- if (!reserved_left[r])
- {
- reserved_left[r] = true;
- swapstart = left - (r + 1) * chunk_size;
- break;
- }
- omp_unset_lock(&result_lock);
- #if _GLIBCXX_ASSERTIONS
- _GLIBCXX_PARALLEL_ASSERT(swapstart != -1);
- #endif
- std::swap_ranges(begin + thread_left_border
- - (chunk_size - 1),
- begin + thread_left_border + 1,
- begin + swapstart);
- }
- if (thread_right >= thread_right_border
- && thread_right_border > rightnew)
- {
- // Find spot and swap
- difference_type swapstart = -1;
- omp_set_lock(&result_lock);
- for (int r = 0; r < leftover_right; ++r)
- if (!reserved_right[r])
- {
- reserved_right[r] = true;
- swapstart = right + r * chunk_size + 1;
- break;
- }
- omp_unset_lock(&result_lock);
- #if _GLIBCXX_ASSERTIONS
- _GLIBCXX_PARALLEL_ASSERT(swapstart != -1);
- #endif
- std::swap_ranges(begin + thread_right_border,
- begin + thread_right_border + chunk_size,
- begin + swapstart);
- }
- #if _GLIBCXX_ASSERTIONS
- # pragma omp barrier
- # pragma omp single
- {
- for (int r = 0; r < leftover_left; ++r)
- _GLIBCXX_PARALLEL_ASSERT(reserved_left[r]);
- for (int r = 0; r < leftover_right; ++r)
- _GLIBCXX_PARALLEL_ASSERT(reserved_right[r]);
- }
- # pragma omp barrier
- #endif
- # pragma omp barrier
- left = leftnew;
- right = rightnew;
- }
- # pragma omp flush(left, right)
- } // end "recursion" //parallel
- difference_type final_left = left, final_right = right;
- while (final_left < final_right)
- {
- // Go right until key is geq than pivot.
- while (pred(begin[final_left]) && final_left < final_right)
- ++final_left;
- // Go left until key is less than pivot.
- while (!pred(begin[final_right]) && final_left < final_right)
- --final_right;
- if (final_left == final_right)
- break;
- std::swap(begin[final_left], begin[final_right]);
- ++final_left;
- --final_right;
- }
- // All elements on the left side are < piv, all elements on the
- // right are >= piv
- delete[] reserved_left;
- delete[] reserved_right;
- omp_destroy_lock(&result_lock);
- // Element "between" final_left and final_right might not have
- // been regarded yet
- if (final_left < n && !pred(begin[final_left]))
- // Really swapped.
- return final_left;
- else
- return final_left + 1;
- }
- /**
- * @brief Parallel implementation of std::nth_element().
- * @param begin Begin iterator of input sequence.
- * @param nth Iterator of element that must be in position afterwards.
- * @param end End iterator of input sequence.
- * @param comp Comparator.
- */
- template<typename RandomAccessIterator, typename Comparator>
- void
- parallel_nth_element(RandomAccessIterator begin, RandomAccessIterator nth,
- RandomAccessIterator end, Comparator comp)
- {
- typedef std::iterator_traits<RandomAccessIterator> traits_type;
- typedef typename traits_type::value_type value_type;
- typedef typename traits_type::difference_type difference_type;
- _GLIBCXX_CALL(end - begin)
- RandomAccessIterator split;
- random_number rng;
- const _Settings& __s = _Settings::get();
- difference_type minimum_length = std::max<difference_type>(2,
- std::max(__s.nth_element_minimal_n, __s.partition_minimal_n));
- // Break if input range to small.
- while (static_cast<sequence_index_t>(end - begin) >= minimum_length)
- {
- difference_type n = end - begin;
- RandomAccessIterator pivot_pos = begin + rng(n);
- // Swap pivot_pos value to end.
- if (pivot_pos != (end - 1))
- std::swap(*pivot_pos, *(end - 1));
- pivot_pos = end - 1;
- // XXX Comparator must have first_value_type, second_value_type,
- // result_type
- // Comparator == __gnu_parallel::lexicographic<S, int,
- // __gnu_parallel::less<S, S> >
- // pivot_pos == std::pair<S, int>*
- // XXX binder2nd only for RandomAccessIterators??
- __gnu_parallel::binder2nd<Comparator, value_type, value_type, bool>
- pred(comp, *pivot_pos);
- // Divide, leave pivot unchanged in last place.
- RandomAccessIterator split_pos1, split_pos2;
- split_pos1 = begin + parallel_partition(begin, end - 1, pred,
- get_max_threads());
- // Left side: < pivot_pos; right side: >= pivot_pos
- // Swap pivot back to middle.
- if (split_pos1 != pivot_pos)
- std::swap(*split_pos1, *pivot_pos);
- pivot_pos = split_pos1;
- // In case all elements are equal, split_pos1 == 0
- if ((split_pos1 + 1 - begin) < (n >> 7)
- || (end - split_pos1) < (n >> 7))
- {
- // Very unequal split, one part smaller than one 128th
- // elements not strictly larger than the pivot.
- __gnu_parallel::unary_negate<__gnu_parallel::
- binder1st<Comparator, value_type, value_type, bool>, value_type>
- pred(__gnu_parallel::binder1st<Comparator, value_type,
- value_type, bool>(comp, *pivot_pos));
- // Find other end of pivot-equal range.
- split_pos2 = __gnu_sequential::partition(split_pos1 + 1,
- end, pred);
- }
- else
- // Only skip the pivot.
- split_pos2 = split_pos1 + 1;
- // Compare iterators.
- if (split_pos2 <= nth)
- begin = split_pos2;
- else if (nth < split_pos1)
- end = split_pos1;
- else
- break;
- }
- // Only at most _Settings::partition_minimal_n elements left.
- __gnu_sequential::nth_element(begin, nth, end, comp);
- }
- /** @brief Parallel implementation of std::partial_sort().
- * @param begin Begin iterator of input sequence.
- * @param middle Sort until this position.
- * @param end End iterator of input sequence.
- * @param comp Comparator. */
- template<typename RandomAccessIterator, typename Comparator>
- void
- parallel_partial_sort(RandomAccessIterator begin,
- RandomAccessIterator middle,
- RandomAccessIterator end, Comparator comp)
- {
- parallel_nth_element(begin, middle, end, comp);
- std::sort(begin, middle, comp);
- }
- } //namespace __gnu_parallel
- #undef _GLIBCXX_VOLATILE
- #endif /* _GLIBCXX_PARALLEL_PARTITION_H */
|