Skip to content

Investigate whether we can reduce amount of copying of parameters in large sender expressionsΒ #225

Open
@lewissbaker

Description

@lewissbaker

When building a sender expression, the evaluation of the expression first builds the leaf senders, then passes these as arguments to higher-level senders, which needs to move-construct them into data-members of itself, which might then be passed to another sender algorithm which then needs to move-construct that whole sub-tree of senders into its data-member, etc.

In the end, a leaf operation (and any state it holds) will be move-constructed O(depth) times when incorporated into a sender-tree of a given depth. As sender-trees typically get wider as they go deeper, the number of move-operations needed to build the final tree can be quadratic as the tree is built.

Further, if the tree is built as a single expression, then all of the temporary intermediate senders still exist until the end of the full-expression and so the amount of stack space needed to create a large expression tree can potentially grow quadratically in the size of the expression tree.

For example: consider a binary tree of when_all() operations where leaves are just(X{}):

// For example, a tree of depth 3
when_all(
  when_all(just(X{}), just(X{})),
  when_all(just(X{}), just(X{})));
Depthjust senders
in final tree
when_all senders
in final tree
X temporaries/moves
1101
2214
34312
48732
5161580

If this sender-expression was then passed to a co_await or sync_wait expression, then this results in the operation-state objects being constructed, which then generally moves the state from the final sender tree into an operation-state tree.

However, with the introduction of transform_sender()-based customization, this can result in moving the entire tree again (the default_domain::transform_sender() function returns a new prvalue sender rather than just returning the input reference.

Further, if the pipe syntax is used, then the number of moves of each leaf argument in a sender tree generally increases by 1 as you first need to move the object into a temporary adaptor object, before then moving it into the initial leaf sender.

We should see if we can reduce the number of intermediate objects required for building large trees, if possible, by directly constructing senders using aggregate initialization.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions