Skip to content

NRVO for coroutines #164560

@kelbon

Description

@kelbon

A common problem with typical task implementations in many coroutine libraries is that the memory for the value to be returned is stored both in the coroutine promise and in frame local variables where it is constructed and returned. This introduces duplication, increases the frame size, and adds a call to the move constructor.
This problem is especially acute for large values ​​or types with non-trivial move constructors, such as std strings.

And, ofc, it prevents returning non-movable types from coroutines, which is sad (but actually its possible with new attribute)

This problem can be solved by introducing a new attribute—its design is subject to debate, but the idea is simple: to give the compiler a hint where to construct the value in cases where, in a regular function, it would use the NRVO (named return value optimization)

example:

void bar(std::string&);

dd::task<std::string> foo() {
    std::string s;
    co_await std::suspend_always{};
    bar(s);
    co_await std::suspend_always{};
// in normal function this is NRVO, but in coroutines its promise.return_value(std::move(s))
    co_return s;
}

In my library i created additional tag this_coro::return_place, which is used like that:

void bar(std::string&);
dd::task<std::string> foo() {
    // constructs value in-place in coro promise
    std::string& s = co_await dd::this_coro::return_place;
    co_await std::suspend_always{};
    bar(s);
    co_await std::suspend_always{};
    co_return dd::rvo;
}

without "NRVO":

https://godbolt.org/z/8YfTrc6K9

with "NRVO":

https://godbolt.org/z/K9647ozGY

Metadata

Metadata

Assignees

No one assigned

    Labels

    clangClang issues not falling into any other categorycoroutinesC++20 coroutinesextension:clang

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions