223 lines
10 KiB
Markdown
223 lines
10 KiB
Markdown
[exec.bulk]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [[exec.snd]](exec.snd#exec.bulk)
|
||
|
||
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.bulk)
|
||
|
||
#### 33.9.12.11 execution::bulk, execution::bulk_chunked, and execution::bulk_unchunked [exec.bulk]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4109)
|
||
|
||
bulk, bulk_chunked, and bulk_unchunked run a task repeatedly for every index in an index space[.](#1.sentence-1)
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4113)
|
||
|
||
The names bulk, bulk_chunked, and bulk_unchunked denote pipeable sender adaptor objects[.](#2.sentence-1)
|
||
|
||
Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#2.sentence-2)
|
||
|
||
For subexpressions sndr, policy, shape, and f,
|
||
letPolicy be remove_cvref_t<decltype(policy)>,Shape be decltype(auto(shape)), andFunc be decay_t<decltype((f))>[.](#2.sentence-3)
|
||
|
||
If
|
||
|
||
- [(2.1)](#2.1)
|
||
|
||
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or
|
||
|
||
- [(2.2)](#2.2)
|
||
|
||
is_execution_policy_v<Policy> is false, or
|
||
|
||
- [(2.3)](#2.3)
|
||
|
||
Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7 Arithmetic concepts [concepts.arithmetic]"), or
|
||
|
||
- [(2.4)](#2.4)
|
||
|
||
Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"),
|
||
|
||
*bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#2.sentence-4)
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4136)
|
||
|
||
Otherwise,
|
||
the expression *bulk-algo*(sndr, policy, shape, f) is expression-equivalent to:
|
||
|
||
transform_sender(*get-domain-early*(sndr), *make-sender*(*bulk-algo*, *product-type*<*see below*, Shape, Func>{policy, shape, f}, sndr)) except that sndr is evaluated only once[.](#3.sentence-2)
|
||
|
||
The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"), andconst Policy& otherwise[.](#3.sentence-3)
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150)
|
||
|
||
Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#4.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, bulk_t> is false, then
|
||
the expression bulk.transform_sender(sndr, env) is ill-formed;
|
||
otherwise, it is equivalent to:auto [_, data, child] = sndr;auto& [policy, shape, f] = data;auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)noexcept(noexcept(f(begin, vs...))) {while (begin != end) func(begin++, vs...);}return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
This causes the bulk(sndr, policy, shape, f) sender to be
|
||
expressed in terms of bulk_chunked(sndr, policy, shape, f) when
|
||
it is connected to a receiver whose
|
||
execution domain does not customize bulk[.](#4.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4172)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for bulk_chunked_t as follows:
|
||
|
||
[ð](#lib:impls-for%3cbulk_chunked_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<bulk_chunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
|
||
|
||
The member *impls-for*<bulk_chunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t>) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
|
||
Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above istrue if and only
|
||
if Tag denotes a type other than set_value_t or
|
||
if the expression f(auto(shape), auto(shape), args...) is well-formed[.](#5.sentence-3)
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4210)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for*<bulk_unchunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; };}
|
||
|
||
The member *impls-for*<bulk_unchunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t>) {auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) {for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above
|
||
is true if and only
|
||
if Tag denotes a type other than set_value_t or
|
||
if the expression f(auto(shape), args...) is well-formed[.](#6.sentence-3)
|
||
|
||
[ð](#lib:check-types,impls-for%3cbulk_t%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253)
|
||
|
||
*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (<remove_cvref_t<*data-type*<Sndr>>, Ts&...>)throw *unspecified-exception*();};
|
||
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
|
||
a type derived from exception[.](#7.sentence-1)
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4268)
|
||
|
||
Let the subexpression out_sndr denote
|
||
the result of the invocation*bulk-algo*(sndr, policy, shape, f) or
|
||
an object equal to such, and
|
||
let the subexpression rcvr denote a receiver
|
||
such that the expression connect(out_sndr, rcvr) is well-formed[.](#8.sentence-1)
|
||
|
||
The expression connect(out_sndr, rcvr) has undefined behavior
|
||
unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) that,
|
||
when started:
|
||
|
||
- [(8.1)](#8.1)
|
||
|
||
If sndr has a successful completion, whereargs is a pack of lvalue subexpressions
|
||
referring to the value completion result datums of sndr, or
|
||
decayed copies of those values if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"),
|
||
then:
|
||
|
||
* [(8.1.1)](#8.1.1)
|
||
|
||
If out_sndr also completes successfully, then:
|
||
|
||
+
|
||
[(8.1.1.1)](#8.1.1.1)
|
||
for bulk,
|
||
invokes f(i, args...) for every i of type Shape from 0 to shape;
|
||
|
||
+
|
||
[(8.1.1.2)](#8.1.1.2)
|
||
for bulk_unchunked,
|
||
invokes f(i, args...) for every i of type Shape from 0 to shape;
|
||
*Recommended practice*: The underlying scheduler should execute each iteration
|
||
on a distinct execution agent[.](#8.1.1.2.sentence-2)
|
||
|
||
+
|
||
[(8.1.1.3)](#8.1.1.3)
|
||
for bulk_chunked,
|
||
invokes f(b, e, args...) zero or more times
|
||
with pairs of b and e of type Shape in range [0, shape],
|
||
such that b<e and
|
||
for every i of type Shape from 0 to shape,
|
||
there is exactly one invocation with a pair b and e,
|
||
such that i is in the range [b, e)[.](#8.1.1.3.sentence-1)
|
||
|
||
* [(8.1.2)](#8.1.2)
|
||
|
||
If out_sndr completes with set_error(rcvr, eptr), then
|
||
the asynchronous operation may invoke a subset of
|
||
the invocations of f before the error completion handler is called, and eptr is an exception_ptr containing either:
|
||
+
|
||
[(8.1.2.1)](#8.1.2.1)
|
||
an exception thrown by an invocation of f, or
|
||
|
||
+
|
||
[(8.1.2.2)](#8.1.2.2)
|
||
a bad_alloc exception if
|
||
the implementation fails to allocate required resources, or
|
||
|
||
+
|
||
[(8.1.2.3)](#8.1.2.3)
|
||
an exception derived from runtime_error[.](#8.1.2.sentence-1)
|
||
|
||
* [(8.1.3)](#8.1.3)
|
||
|
||
If out_sndr completes with set_stopped(rcvr), then
|
||
the asynchronous operation may invoke a subset of
|
||
the invocations of f before the stopped completion handler[.](#8.1.3.sentence-1)
|
||
|
||
- [(8.2)](#8.2)
|
||
|
||
If sndr does not complete with set_value, then
|
||
the completion is forwarded to recv[.](#8.2.sentence-1)
|
||
|
||
- [(8.3)](#8.3)
|
||
|
||
For *bulk-algo*,
|
||
the parameter policy describes
|
||
the manner in which
|
||
the execution of the asynchronous operations corresponding to these algorithms
|
||
may be parallelized and
|
||
the manner in which
|
||
they apply f[.](#8.3.sentence-1)
|
||
Permissions and requirements
|
||
on parallel algorithm element access functions ([[algorithms.parallel.exec]](algorithms.parallel.exec "26.3.3 Effect of execution policies on algorithm execution"))
|
||
apply to f[.](#8.3.sentence-2)
|
||
|
||
[9](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4358)
|
||
|
||
[*Note [2](#note-2)*:
|
||
|
||
The asynchronous operation corresponding to*bulk-algo*(sndr, policy, shape, f) can complete with set_stopped if cancellation is requested or
|
||
ignore cancellation requests[.](#9.sentence-1)
|
||
|
||
â *end note*]
|