ompUtils.hpp
This header file defines utility functions for OpenMP parallel operations such as merge, merge sort, reduce, and scan. These functions are part of the sctl::omp_par namespace.
Functions
merge(ConstIter A_begin, ConstIter A_end, ConstIter B_begin, ConstIter B_end, Iter C_begin, Int count, comp): Merges two sorted ranges into a single sorted range.
merge_sort(ConstIter begin, ConstIter end, comp): Performs merge sort on a range using a custom comparison function.
merge_sort(ConstIter begin, ConstIter end): Performs merge sort on a range.
value_type reduce(ConstIter input, Int cnt): Reduces the elements in a range to a single value.
scan(ConstIter input, Iter output, Int cnt): Performs a parallel prefix sum (scan) operation on a range.
#ifndef _SCTL_OMPUTILS_HPP_
#define _SCTL_OMPUTILS_HPP_
#include <iterator> // for iterator_traits
#include "sctl/common.hpp" // for sctl
#include "sctl/iterator.hpp" // for Iterator, ConstIterator
namespace sctl {
namespace omp_par {
/**
* Parallel bytewise copy over generic random-access iterators (raw pointers
* or sctl `Iterator` / `ConstIterator`, which are bounds-checked in MEMDEBUG).
* Byte-wise (memcpy) semantics require contiguous, trivially-copyable storage;
* value types of both iterators must match and be trivially copyable (asserted
* at compile time — contiguity itself cannot be checked before C++20 and is
* the caller's responsibility). For element-wise copy through arbitrary
* iterators, use `omp_par::copy` (or plain `std::copy`).
*
* The thread count is chosen by an empirical heuristic when `nthreads < 0`:
* - bytes < 2 MB → serial (`std::memcpy`)
* - bytes >= 2 MB → full `omp_get_max_threads()`
* Pass `nthreads > 0` to force a specific thread count; pass `1` to force serial.
* If called from inside an `omp parallel` region the heuristic forces serial.
*
* @tparam OutputIt random-access iterator over contiguous storage.
* @tparam InputIt random-access iterator over contiguous storage.
* @param[out] dst destination iterator.
* @param[in] src source iterator.
* @param[in] n number of elements (NOT bytes).
* @param[in] nthreads explicit thread count, or -1 for the heuristic.
*/
template <class OutputIt, class InputIt> void memcpy(OutputIt dst, InputIt src, Long n, Integer nthreads = -1);
/**
* Parallel element-wise copy over random-access iterators. Each chunk is
* dispatched to `std::copy`, so user `operator=` is invoked normally (safe for
* non-trivially-copyable T). Thread-count heuristic is identical to
* `omp_par::memcpy`, using `sizeof(value_type) * (last - first)` as the byte
* budget.
*
* @tparam InputIt random-access input iterator.
* @tparam OutputIt random-access output iterator.
* @param[in] first range begin.
* @param[in] last range end (one past last element).
* @param[out] dst destination range begin.
* @param[in] nthreads explicit thread count, or -1 for the heuristic.
* @return iterator past the last element written: `dst + (last - first)`.
*/
template <class InputIt, class OutputIt> OutputIt copy(InputIt first, InputIt last, OutputIt dst, Integer nthreads = -1);
/**
* Merges two sorted ranges into a single sorted range.
*
* @tparam ConstIter Iterator type for the input ranges.
* @tparam Iter Iterator type for the output range.
* @tparam Int Integer type for indexing.
* @tparam StrictWeakOrdering Functor type for comparing elements.
*
* @param A_ Beginning iterator of the first range.
* @param A_last Ending iterator of the first range.
* @param B_ Beginning iterator of the second range.
* @param B_last Ending iterator of the second range.
* @param C_ Beginning iterator of the output range.
* @param p Integer indicating the parallelism level.
* @param comp Functor for comparing elements.
*/
template <class ConstIter, class Iter, class Int, class StrictWeakOrdering> void merge(ConstIter A_, ConstIter A_last, ConstIter B_, ConstIter B_last, Iter C_, Int p, StrictWeakOrdering comp);
/**
* Performs merge sort on a range using a custom comparison function.
*
* @tparam T Iterator type for the input range.
* @tparam StrictWeakOrdering Functor type for comparing elements.
*
* @param A Beginning iterator of the range.
* @param A_last Ending iterator of the range.
* @param comp Functor for comparing elements.
*/
template <class T, class StrictWeakOrdering> void merge_sort(T A, T A_last, StrictWeakOrdering comp);
/**
* Performs merge sort on a range.
*
* @tparam T Iterator type for the input range.
*
* @param A Beginning iterator of the range.
* @param A_last Ending iterator of the range.
*/
template <class T> void merge_sort(T A, T A_last);
/**
* Parallel sample sort. Writes the sorted output to a separate buffer B (out-of-place).
* Scales better than merge_sort for large records by bounding data movement (one
* bucket scatter + per-bucket std::sort) rather than O(log p) full-array merge passes.
*
* @param A Beginning iterator of the (unmodified) input range.
* @param B Beginning iterator of the output range (size N).
* @param N Number of elements.
* @param comp Functor for comparing elements.
*/
template <class ConstIter, class Iter, class StrictWeakOrdering> void sample_sort(ConstIter A, Iter B, Long N, StrictWeakOrdering comp);
/**
* In-place parallel sample sort (sorts the range [A, A_last) in place using an
* internal scratch buffer). Drop-in replacement for merge_sort.
*
* @param A Beginning iterator of the range.
* @param A_last Ending iterator of the range.
* @param comp Functor for comparing elements.
*/
template <class T, class StrictWeakOrdering> void sample_sort(T A, T A_last, StrictWeakOrdering comp);
/**
* In-place parallel sample sort using the default (operator<) ordering.
*
* @param A Beginning iterator of the range.
* @param A_last Ending iterator of the range.
*/
template <class T> void sample_sort(T A, T A_last);
/**
* Parallel multiway merge of `nruns` sorted runs into a single sorted output. The runs are the
* contiguous segments [run_dsp[d], run_dsp[d+1]) of `runs` (run_dsp has nruns+1 entries, with
* run_dsp[0] the start offset and run_dsp[nruns] the total count); `out` (that many elements)
* receives their merge. Parallelized by splitting the output into per-thread contiguous chunks
* via sampled splitters, each thread heap-merging its chunk's sub-runs -- single pass, no extra
* copies, exploits the pre-sortedness (O((N/p)*log nruns) vs O((N/p)*log(N/p)) for a re-sort).
*
* @param runs Beginning iterator of the buffer holding the concatenated sorted runs.
* @param run_dsp Run boundary offsets into `runs` (nruns+1 entries, ascending).
* @param nruns Number of runs.
* @param out Beginning iterator of the output range (size run_dsp[nruns]).
* @param comp Functor for comparing elements.
*/
template <class ConstIter, class Iter, class StrictWeakOrdering> void multiway_merge(ConstIter runs, ConstIterator<Long> run_dsp, Long nruns, Iter out, StrictWeakOrdering comp);
/**
* Reduces the elements in a range to a single value.
*
* @tparam ConstIter Iterator type for the input range.
* @tparam Int Integer type for indexing.
*
* @param A Beginning iterator of the range.
* @param cnt Number of elements in the range.
* @return The reduced value.
*/
template <class ConstIter, class Int> typename std::iterator_traits<ConstIter>::value_type reduce(ConstIter A, Int cnt);
/**
* Parallel **exclusive** prefix sum with caller-supplied seed.
*
* Computes
*
* B[0] is left untouched (must be initialised by the caller),
* B[i] = B[0] + A[0] + A[1] + ... + A[i-1] for i = 1, ..., cnt-1.
*
* Equivalently, `B[i]` is the sum of the first `i` elements of `A` shifted by
* the caller-supplied initial value at `B[0]`. With the conventional seed
* `B[0] = 0`, this is a standard exclusive prefix sum (e.g. converting a
* count array into a displacement array). The function **does not write**
* `B[0]`, so leaving it uninitialised is a bug.
*
* `A[cnt-1]` is not read; only `A[0..cnt-2]` participate. Aliasing `A` and
* `B` is not supported.
*
* @tparam ConstIter Iterator type for the input range.
* @tparam Iter Iterator type for the output range.
* @tparam Int Integer type for indexing.
*
* @param[in] A Beginning iterator of the input range (length `cnt`).
* @param[in,out] B Beginning iterator of the output range (length `cnt`); `B[0]` is read as the seed and must be initialised before the call; `B[1..cnt-1]` are written.
* @param[in] cnt Number of elements in each range.
*/
template <class ConstIter, class Iter, class Int> void scan(ConstIter A, Iter B, Int cnt);
} // end namespace omp_par
} // end namespace sctl
#endif // _SCTL_OMPUTILS_HPP_