+ All Categories
Home > Documents > P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that...

P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that...

Date post: 05-Jul-2020
Category:
Upload: others
View: 4 times
Download: 0 times
Share this document with a friend
40
P0889: Ultimate copy elisions and P0878R0: Subobjects copy elision Antony Polukhin
Transcript
Page 1: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

P0889: Ultimate copy elisionsand

P0878R0: Subobjects copy elision

Antony Polukhin

Page 2: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Copy/Move elision

Page 3: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Copy/Move elision

Simplified description:

Copy elision allows to not call copy/move constructors when returning a value of function return type from a function

3 / 50

Page 4: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Copy/Move elision

Simplified description:

Copy elision allows to not call copy/move constructors when returning a value of function return type from a function

Precise description available at [class.copy.elision]

4 / 50

Page 5: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Some codestruct T {

T() noexcept; T(T&&) noexcept; ~T() noexcept;

void do_something() noexcept;

};

static T produce() { T a; a.do_something(); return a; }

static T update(T b) { b.do_something(); return b; }

static T shrink(T c) { c.do_something(); return c; }

int caller() {

T d = shrink(update(produce()));

} 5 / 50

Page 6: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Current result of compilationcaller():

sub rsp, 24

lea rdi, [rsp+14]

call T::T()

lea rdi, [rsp+14]

call T::do_something()

lea rdi, [rsp+14]

call T::do_something()

lea rsi, [rsp+14]

lea rdi, [rsp+15]

call T::T(T&&)

lea rdi, [rsp+15] 6 / 50

call T::do_something()

lea rsi, [rsp+15]

lea rdi, [rsp+13]

call T::T(T&&)

lea rdi, [rsp+15]

call T::~T()

lea rdi, [rsp+14]

call T::~T()

lea rdi, [rsp+13]

call T::~T()

Page 7: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Current result of compilationcaller():

call T::T() // T a

call T::do_something() // a.do_something();

// T& b = a; // copy elision worked well

call T::do_something() // b.do_something();

call T::T(T&&) // T c{std::move(b)};

call T::do_something() // c.do_something();

call T::T(T&&) // T d{std::move(c)};

call T::~T() // ~d();

call T::~T() // ~c();

call T::~T() // ~a(); /*~b()*/

7 / 50

Page 8: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Current result of compilationcaller():

call T::T() // T a

call T::do_something() // a.do_something();

// T& b = a; // copy elision worked well

call T::do_something() // b.do_something();

call T::T(T&&) // T c{std::move(b)};

call T::do_something() // c.do_something();

call T::T(T&&) // T d{std::move(c)};

call T::~T() // ~d();

call T::~T() // ~c(); `c` isn't accessed after move construction of `d`←

call T::~T() // ~a(); `a` isn't accessed after move construction of `c`←

8 / 50

Page 9: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

There's something strange

In assembly there is a following pattern:

Variable X is copy constructed from variable Y

Variable Y is not accessed any more

Variable Y is destroyed

9 / 50

Page 10: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

There's something strange

In assembly there is a following pattern:

Variable X is copy constructed from variable Y

Variable Y is not accessed any more

Variable Y is destroyed

Instead if copying and using the copy compiler could reuse the old object as if it was a new one (do the copy elision) [*]

10 / 50

Page 11: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Better result is possible:caller():

call T::T() // T a

call T::do_something() // a.do_something();

call T::do_something() // ba.do_something();

call T::T(T&&) // T c{std::move(b)};

call T::do_something() // ca.do_something();

call T::T(T&&) // T d{std::move(c)};

call T::~T() // ~d();

call T::~T() // ~c();

call T::~T() // ~a();

11 / 50

Page 12: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Better result is almost 2 times shorter:caller():

call T::T() // T a

call T::do_something() // a.do_something();

call T::do_something() // a.do_something();

call T::do_something() // a.do_something();

call T::~T() // ~a();

12 / 50

Page 13: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

The idea: Relaxed rules for CE

Allow to reuse the old object as if it was a new one if the old object is not accessed between a copy/move construction of it and its destruction.

Allow modern compilers to mix copy elision optimization with results of other optimizations.

13 / 50

Page 14: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Page 15: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

15 / 50

Page 16: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

16 / 50

Page 17: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

Compilers fail to inline the copy/move constructors and optimize the whole code to the same point as the relaxed copy elision rules may allow

17 / 50

Page 18: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

Compilers fail to inline the copy/move constructors and optimize the whole code to the same point as the relaxed copy elision rules may allow:

Affects runtime performance

Affects binary sizes

18 / 50

Page 19: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

Compilers fail to inline the copy/move constructors and optimize the whole code to the same point as the relaxed copy elision rules may allow:

Affects runtime performance

Affects binary sizes

Because compilers do not elide copy constructors users are forced to write std::move

19 / 50

Page 20: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

Compilers fail to inline the copy/move constructors and optimize the whole code to the same point as the relaxed copy elision rules may allow:

Affects runtime performance

Affects binary sizes

Because compilers do not elide copy constructors users are forced to write std::move

Rules for writing (or not writing) std::move are tricky

20 / 50

Page 21: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Problems that P0889 addresses

Compilers are forced to inline constructors+destructors and optimize a lot of IR

Affects compile times

Compilers fail to inline the copy/move constructors and optimize the whole code to the same point as the relaxed copy elision rules may allow:

Affects runtime performance

Affects binary sizes

Because compilers do not elide copy constructors users are forced to write std::move

Rules for writing (or not writing) std::move are tricky

Affects language usage simplicity and teach-ability

21 / 50

Page 22: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

How far we should go and what could be improved?

Page 23: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Copy elisions through references

return std::move(local_variable);

auto& v = local_variable; return v;

return path(__lhs) /= __rhs; // libstdc++/85671

???

23 / 50

Page 24: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Decompose default destructible types

return pair.second;

return get<0>(tuple); // requires CE through references

auto [a,b] = foo(); return a; // requires CE through references

return local_aggregate_variable.name;

24 / 50

Page 25: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Decompose any type

return stringstream.str(); // requires CE through references

return get<int>(variant); // requires CE through references

return path.string();

25 / 50

Page 26: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Not only for function returns

{ T v; takes_by_copy(v); }

{ T v; takes_by_reference_and_copies_internally(v); }

struct B { T a; B(const T& a): a(a) {} }; B b{T{}}; // CWG #1049

26 / 50

Page 27: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

All together (P0889)

[class.copy.elision]

...

Additionally, copy elision is allowed for any non-volatile object with automatic storage duration and it's non-volatile nested objects if source is not accessed between a copy/move construction of it and its destruction.

27 / 50

Page 28: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

How far we should go?

Allow to do copy elisions through references?

Allow to decompose default destructible types and do copy elisions for subobjects? (P0878R0 “V. Proposed wording 1”)

Allow to decompose and do copy elisions for subobjects? (P0878R0 “V. Proposed wording 2”)

Allow elisions to work not only for functions' returns?

Allow all of the above? (P0889R0 “VI. Proposed wording”)

28 / 50

Page 29: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

FAQ

Page 30: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

[*] Does it break user code?

Page 31: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

[*] Does it break user code?

Yes

31 / 50

Page 32: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

[*] Does it break user code?

Yes, I'm 100% sure

32 / 50

Page 33: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

[*] Does it break user code?

Yes, I'm 100% sure

Good news: it breaks only unportable code

Code where generally-accepted constraint for a copy constructor is not satisfied: "After the definition T u = v;, u is equal to v".

33 / 50

Page 34: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Why the code is unportable?

Language and Library heavily rely on “After the definition T u = v;, u is equal to v”

Ranges TS force that requirement

Library implicitly requires objects after copy/assignment/move to be equal in [container.requirements.general]

Algorithms do not work well if that constraint is not satisfied

Complexities are described as "At most [...] swaps" or "Approximately [...] swaps"

Algorithms sometimes do not specify the order of copying/swapping

[class.copy.elision] implicitly relies on that constraint

[class.copy.elision] is not mandatory!

Guaranteed copy elision implicitly relied on that constraint34 / 50

Page 35: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Why the code is unportable?

C++ Language and Library heavily rely on “After the definition T u = v;, u is equal to v”

WG21 has been relying on that constraint for a long time and classes that violate that constraint are already unportable across platforms/Standard versions.

35 / 50

Page 36: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Is it important?

Page 37: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Are those problems important for users?

Runtime performance

Binary sizes

Compile times

Language usage simplicity and teach-ability

37 / 50

Page 38: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Those problems are important for users!

Runtime performance

Binary sizes

Compile times

Language usage simplicity and teach-ability

Here are only some EWG papers from 2018 mailings that are related to those problems:

[[move_relocates]], [[likely]], trivial virtual destructors, zero-overhead exceptions, [[no_unique_address]], Modules, down with typename …

There's even more papers for LEWG that try to improve some of those

38 / 50

Page 39: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Is it possible to implement right now?

Page 40: P0889: Ultimate copy elisions and P0878R0: Subobjects copy ... elisions v2.pdf · Problems that P0889 addresses Compilers are forced to inline constructors+destructors and optimize

Is it possible to implement right now?

Yes, but it would be hard.

Anyway, this proposal does not require any of the optimizations from examples. The proposal simply attempts to relax copy elision rules to allow those optimizations someday.

40 / 50


Recommended