In structured parallel programming, algorithms are often defined recursively in a divide-and-conquer fashion.
The proposal P2300 that is currently targeted for C++26 aims to provide a modern solid foundation for asynchronous and parallel programming with C++, based around the concepts senders, schedulers and sender adaptors.
The documentation of P2300, libunifex as well as the examples are mostly concerned with the asynchronous and less about the parallel use case.
I managed to realize a recursive parallel quicksort based on libunifex:
any_sender_of<> quicksort(scheduler auto sch, std::random_access_iterator auto begin, std::random_access_iterator auto end)
{
size_t N = std::ranges::distance(begin, end);
constexpr size_t parallelCutOff = 128;
if ( N <= parallelCutOff ) {
std::cout << "Fullfilling batch from thread " << std::this_thread::get_id() << "." << std::endl;
std::sort(begin, end);
return just();
}
auto pivot = begin; // don't do this in production code!
pivot = std::partition(begin, end, [=](auto &&e) { return e < *pivot; });
auto pipe_begin = schedule(sch);
return when_all(
let_value(pipe_begin, [=]() { return quicksort(sch, begin, pivot); }),
let_value(pipe_begin, [=]() { return quicksort(sch, pivot + 1, end); })
) | then([](auto&&...){});
}
int main()
{
std::vector<int> v = //...;
scheduler auto sch = thread_pool.get_scheduler();
sync_wait(quicksort(sch, v.begin(), v.end()));
std::ranges::copy(v, std::ostream_iterator<size_t>(std::cout, ", "));
}
This implementation however
- needs
any_sender_of<>
as return type which is not part of P2300 and afaiu does type-erasure and therfore requires heap-allocation(?), which kills the performance - takes a scheduler as argument not a sender and therefore does not allow for composition with other senders
What is the envisioned, ideomatic way to implement recursive algorithms using P2300?