std::execution::bulk, std::execution::bulk_chunked, std::execution::bulk_unchunked

From cppreference.net
< cpp‎ | execution
 
 
 
Defined in header <execution>
std::execution::sender

auto bulk( std::execution::sender auto input,
           std::integral auto size,
           auto&& policy,
           std::invocable<decltype(size),

                          /*values-sent-by*/(input)...> function );
(1) (since C++26)
std::execution::sender

auto bulk_chunked( std::execution::sender auto input,
                   std::integral auto size,
                   auto&& policy,
                   std::invocable<decltype(size), decltype(size),

                                  /*values-sent-by*/(input)...> function2 );
(2) (since C++26)
std::execution::sender

auto bulk_unchunked( std::execution::sender auto input,
                     std::integral auto size,
                     std::invocable<decltype(size), decltype(size),

                                    /*values-sent-by*/(input)...> function );
(3) (since C++26)

Parameters

input - sender which once executed sends the values upon which the function executes
policy - the execution policy attached to function/function2
function - invocable to be called for every index in range [0size), passing also the values produced by the input sender
function2 - same as function but called with a pair of indices (be), with b < e, so that, for each index i in range [[0size) there is exactly one call to function2 so that b <= i < e.

Return value

Returns a sender describing the task graph described by the input sender, with an added node of invoking the provided function with indices in range [0size), passing also the values sent by the input sender as arguments.

function/function2 is guaranteed to not begin executing until the returned sender is started.

Error completions

All errors passed in by input are forwarded.

In addition, the sender is allowed to complete with an std::exception_ptr error that contains:

  • any exception thrown by function
  • std::bad_alloc if the implementation fails to allocate required resources
  • an exception derived from std::runtime_error for other internal errors (e.g., cannot propagate the exception from the execution context to the caller).

Cancellation

The uncustomized std::execution::bulk, std::execution::bulk_chunk and std::execution::bulk_unchunked forward the stopped completion signal from input. They do not provide additional mechanism to produce stopped completion signal.

Notes

When calling std::execution::bulk and std::execution::bulk_chunked, different invocations of function/function2 may happen on the same execution agent.

When calling std::execution::bulk_unchunked, different invocations of function must happen on different execution agents.

The default implementation of std::execution::bulk is based on std::execution::bulk_chunked. While customizing std::execution::bulk is possible, it is expected that most of the time only std::execution::bulk_chunked is customized.

Without a customization of std::execution::bulk and std::execution::bulk_chunked, the behavior of std::execution::bulk and std::execution::bulk_chunk is to execute function serially, which is not particularly useful. Implementations are expected to have customizations that would make running std::execution::bulk and std::execution::bulk_chunked on different schedulers more useful.

std::execution::bulk_unchunked is meant to be used whenever function may have dependencies between different invocations, and it requires concurrent forward progress guarantees (parallel forward progress is not enough). Running std::execution::bulk_unchunked with a size of 1000 will require 1000 execution agents (e.g., threads) to run concurrently.

std::execution::bulk_unchunked does not require an execution policy, as is already expected for function to be able to run concurrently.

Examples

Possible usage of execution::bulk.

std::vector<double> x;
std::vector<double> y;
//...
sender auto process_elements
    = just(get_coefficient())
    | bulk(x.size(), [&](size_t i, double a)
    {
        y[i] = a * x[i] + y[i];
    });
// process_elements describes the work described by calling a function to
// get a coefficient `a`, and using it to execute
//   y[i] = a * x[i] + y[i]
// for each `i` in range [0, x.size())

Possible usage of execution::bulk_chunked.

std::vector<std::uint32_t> data = ...;
std::atomic<std::uint32_t> sum{0};
sender auto s = bulk_chunked(just(), par, 100000,
    [&sum, &data](int begin, int end)
    {
        auto partial_sum = std::accumulate(data.begin() + begin, data.begin() + end, 0U);
        sum.fetch_add(partial_sum);
    });
// the atomic object will not be touched 100000 times; will execute faster than bulk()