faster c move construction and perfect forwarding n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Faster C++: Move Construction and Perfect Forwarding PowerPoint Presentation
Download Presentation
Faster C++: Move Construction and Perfect Forwarding

Loading in 2 Seconds...

play fullscreen
1 / 42

Faster C++: Move Construction and Perfect Forwarding - PowerPoint PPT Presentation


  • 85 Views
  • Uploaded on

Faster C++: Move Construction and Perfect Forwarding. Pete Isensee Advanced Technology Group Microsoft. Problem Statement. Copying deep objects is expensive C++ is built on copy semantics STL containers store by value Compiler temporaries are copied by value

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Faster C++: Move Construction and Perfect Forwarding' - haines


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
faster c move construction and perfect forwarding

Faster C++:Move Construction and Perfect Forwarding

Pete Isensee

Advanced Technology Group

Microsoft

problem statement
Problem Statement
  • Copying deep objects is expensive
  • C++ is built on copy semantics
    • STL containers store by value
    • Compiler temporaries are copied by value
  • Copying is often non-obvious in source code
  • Games copy objects – a lot!
example
Example

Deep

struct Texture {

unsigned long mSize;

unsigned long* mpBits;

};

Shallow

Deeper

struct Particle {

Vector3 mPos;

Vector3 mVel;

Color mCol;

};

structParticleSystem {

std::vector< Particle > mPar;

Texture mTex;

};

ParticleSystemparticleSys(...);

particleSys = StartExplosion(); // Explosion begins

particleSys += AddSmoke(); // More particles added

slide4

ParticleSystemparticleSys(...);

particleSys = StartExplosion(); // Explosion begins

particleSys += AddSmoke(); // More particles added

slide5

ParticleSystemparticleSys(...);

particleSys = StartExplosion(); // Explosion begins

particleSys += AddSmoke(); // More particles added

particleSys

StartExplosion()

v

v

v

v

v

v

t

t

t

t

t

t

v

t

v

t

copying temp o bjects is expensive
Copying Temp Objects is Expensive

Perfof operator=(constParticleSystem&)

avoiding temporaries is difficult
Avoiding Temporaries is Difficult

bool Connect( conststd::string& server, ... );

if( Connect( “microsoft.com” ) ) // temporary object created

v.push_back( X(...) ); // another temporary

a = b + c; // b + c is a temporary object

x++; // returns a temporary object

a = b + c + d; // c+d is a temporary object

// b+(c+d) is another temporary object

what we would like
What We Would Like…
  • Is a world where…
    • We could avoid unnecessary copies
    • In cases where it was safe to do so
    • Completely under programmer control
  • For example…
slide9

ParticleSystemparticleSys(...);

particleSys = StartExplosion(); // Explosion begins

particleSys += AddSmoke(); // More particles added

particleSys

StartExplosion()

v

v

v

v

t

t

t

t

v

v

t

t

v

v

t

t

consider assignment
Consider Assignment

structParticleSystem {

std::vector< Particle > mPar;

Texture mTex;

};

Canonical copy assignment

What we want

ParticleSystem& operator=( constParticleSystem& rhs ) {

if( this != &rhs ) {

mPar = rhs.mPar; // Vector assignment (copy)

mTex= rhs.mTex; // Texture assignment (copy)

}

return *this;

}

ParticleSystem& operator=( <Magic Type>rhs ) {

// move semantics here ...

return *this;

}

solution c 11 standard to the rescue
Solution: C++11 Standard to the Rescue
  • Don’t copy when you don’t need to; move instead
  • Critically important for deep objects
  • Key new language feature: rvalue references
  • Enables move semantics, including
    • Move construction
    • Move assignment
    • Perfect forwarding
example1
Example

Copy assignment

Move assignment

ParticleSystem& operator=( constParticleSystem& rhs ) {

if( this != &rhs ) {

mPar = rhs.mPar; // Particle vector copy

mTex= rhs.mTex; // Texture copy

}

return *this;

}

ParticleSystem& operator=( ParticleSystem&& rhs ) {

if( this != &rhs ) {

mPar = std::move( rhs.mPar ); // Vector move

mTex= std::move( rhs.mTex ); // Texture move

}

return *this;

}

movable objects rvalues
Movable Objects: rvalues
  • Move from = eviscerate thyself
  • Every expression is either an lvalue or rvalue
  • Always safe to move from an rvalue
lvalue r value examples
lvalue/rvalue examples

++x; // lvalue

int a; // a is an lvalue

X x; // x is an lvalue

x++; // rvalue

X(); // X() is an rvalue

*ptr// lvalue

int a = 1+2; // a is an lvalue; 1+2 is an rvalue

foo( x ); // x is an lvalue

x+42 // rvalue

“abc” // lvalue

foo( bar() ); // bar() is an rvalue

4321 // rvalue

std::string( “abc” ) // rvalue

rvalue references
rvalue References

T&&

  • T&: reference (pre C++11)
  • T&: lvalue reference in C++11
  • T&&: rvalue reference; new in C++11
  • rvalue references indicate objects that can be safely moved from
  • rvalue references bind to rvalue expressions
  • lvalue references bind to lvalue expressions
binding
Binding

foo( ParticleSystem&& ); // A: rvalue

foo( constParticleSystem&& ); // B: constrvalue

foo( ParticleSystem& ); // C: lvalue

foo( constParticleSystem& ); // D: constlvalue

ParticleSystemparticleSys;

constParticleSystemcparticleSys;

foo( particleSys ); // lvalue

foo( StartExplosion() ); // rvalue

foo( cparticleSys ); // constlvalue

std move
std::move

ParticleSystem& operator=( ParticleSystem&& rhs ) {

if( this != &rhs ) {

mPar = std::move( rhs.mPar ); // Vector move assignment

mTex= std::move( rhs.mTex ); // Texture move assignment

}

return *this;

}

  • std::move ~= static_cast< T&& >(t)
  • Tells compiler: treat this named variable as an rvalue
  • Highly complex implementation due to reference collapsing, parameter deduction and other arcane language rules

template< class T > inline typenamestd::remove_reference<T>::type&&

move( T&& t ) noexcept {

using ReturnType = typenamestd::remove_reference<T>::type&&;

return static_cast< ReturnType >( t );

}

move assign
Move assign

ParticleSystem& operator=( ParticleSystem&& rhs ) {

if( this != &rhs ) {

mPar = std::move( rhs.mPar ); // Vector move assignment

mTex= std::move( rhs.mTex ); // Texture move assignment

}

return *this;

}

std::vector<T>& operator=( std::vector<T>&& rhs ) {

if( this != &rhs ) {

DestroyRange( mpFirst, mpLast ); // call all dtors

if( mpFirst != nullptr ) free( mpFirst );

mpFirst = rhs.mpFirst; // eviscerate

mpLast = rhs.mpLast;

mpEnd = rhs.mpEnd; // rhs now empty shell

rhs.mpFirst = rhs.mpLast = rhs.mpEnd = nullptr;

}

return *this;

}

// Standard assignment operator

Texture& Texture::operator=( const Texture& rhs ) {

if( this != &rhs ) {

if( mpBits != nullptr) free( mpBits );

mSize = rhs.mSize;

mpBits = malloc( mSize );

memcpy( mpBits, rhs.mpBits, mSize );

}

return *this;

}

Texture& Texture::operator=( Texture&& rhs ) {

if( this != &rhs ) {

if( mpBits != nullptr ) free( mpBits );

mpBits = rhs.mpBits; // eviscerate

mSize = rhs.mSize;

rhs.mpBits = nullptr; // clear rhs

}

return *this;

}

intermission
Intermission
  • Use rvalue reference semantics to enable moves
  • Use non-constrvalue: rhs is reset
  • std::move tells compiler “this is really an rvalue”
  • Binding rules allow gradual conversion
    • Implement rvalue reference semantics as you go
    • Start in low-level libraries
    • Or start in high-level code, your choice
performance revisited
Performance Revisited

operator=(constParticleSystem&)

operator=(ParticleSystem&&)

move constructors
Move Constructors

vector<T>::vector( vector<T>&& rhs ) :

mpFirst( rhs.mpFirst ), // eviscerate

mpLast ( rhs.mpLast),

mpEnd ( rhs.mpEnd)

{

// rhs now an empty shell

rhs.mpFirst = rhs.mpLast = rhs.mpEnd = nullptr;

}

Texture::Texture( Texture&& rhs ) :

mpBits( rhs.mpBits ), // eviscerate

mSize( rhs.mSize )

{

// rhs now an empty shell

rhs.mpBits = nullptr;

}

ParticleSystem::ParticleSystem( ParticleSystem&& rhs ) :

// invoke member move ctors

mPar( std::move( rhs.mPar ) ),

mTex( std::move( rhs.mTex ) )

{

}

perfect forwarding problem
Perfect Forwarding Problem

Suppose we have some setter functions

void ParticleSystem::SetTexture( const Texture& texture ) {

mTex = texture; // We’d like to move if tx is a temporary

}

void ParticleSystem::SetTexture( Texture&& texture ) {

mTex = std::move( texture ); // Move

}

void ParticleSystem::Set( constA& a, const B& b ) {

// Uh-oh, we need three new overloads...

}

func templates plus rvalues to the rescue
Func Templates Plus rvalues to the Rescue

Powerful new rule in C++11. Given:

Template rvalue ref param binds to anything

template< typename T > void f( T&& t ); // template function

binding rvalue reference template params
Binding rvalue Reference Template Params

Examples

template< typename T > void f( T&& t ); // template function

int a;

constintca = 42;

f( a ); // instantiates f( int& );

f( ca ); // instantiates f( constint& );

f( StartExplosion() ); // instantiates f( ParticleSystem&& );

perfect forwarding
Perfect Forwarding

template< typename T >

void ParticleSystem::SetTexture( T&& texture ) {

mTex = std::forward<T>( texture ); // invokes right overload

}

std::forward<T> equivalent to

  • static_cast<[const] T&&>(t) when t is an rvalue
  • static_cast<[const] T&>(t) when t is an lvalue

template< class T > inline T&& // typical std::forward implementation

forward( typename identity<T>::type& t ) noexcept {

return static_cast<T&&>( t );

}

perfect constructors
Perfect Constructors

Typical multi-argctor; doesn’t handle rvalues

ParticleSystem::ParticleSystem( conststd::vector<Particle>& par,

const Texture& texture ) :

mPar( par ),

mTex( texture ) {

}

Perfect constructor; handles everything you throw at it!

template< typename V, typename T >

ParticleSystem::ParticleSystem( V&& par, T&& texture ) :

mPar( std::forward<V>( par ) ),

mTex( std::forward<T>( texture ) ) {

}

implicit special member functions
Implicit Special Member Functions
  • Rule of Three: if you define any of the first three, define all
  • Rule of Two Moves: If you define either move, define both
be explicit about implicit special functions
Be Explicit About Implicit Special Functions

structParticleSystem {

std::vector< Particle > mPar; // Copyable/movable object

Texture mTex; // Copyable/movable object

// Ctors

ParticleSystem() = delete;

ParticleSystem( constParticleSystem& ) = default;

ParticleSystem( ParticleSystem&& ) = default;

// Assign

ParticleSystem& operator=( constParticleSystem& ) = default;

ParticleSystem& operator=( ParticleSystem&& ) = default;

// Destruction

~ParticleSystem() = default;

};

slide30
C++11

template< typename T >

swap( T& a, T& b ) {

T tmp( std::move( a ) );

a = std::move( b );

b = std::move( tmp );

}

  • STL containers move enabled
    • Including std::string
  • STL algorithms move enabled
    • Including sort, partition, swap
  • You get immediate speed advantages simply by recompiling
recommended idioms moveable types
Recommended Idioms: Moveable Types

struct Deep {

Deep( const Deep& ); // Copy ctor

Deep( Deep&& ); // Move ctor

template< typename A, typename B >

Deep( A&&, B&& ); // Perfect forwarding ctor

Deep& operator=( const Deep& ); // Copy assignment

Deep& operator=( Deep&& ); // Move assignment

~Deep();

template< typename A > // Deep setters

void SetA( A&& );

};

recommended idioms raw pointers
Recommended Idioms: Raw Pointers

T( T&& rhs ) :

ptr( rhs.ptr ) // eviscerate

{

rhs.ptr = nullptr; // rhs: safe state

}

Move ctor

T& operator=( T&& rhs ) {

if( this != &rhs ) {

if( ptr != nullptr )

free( ptr );

ptr = rhs.ptr; // eviscerate

rhs.ptr = nullptr; // rhs: safe state

}

return *this;

}

Move assignment

recommended idioms higher level objs
Recommended Idioms: Higher Level Objs

T( T&& rhs ) :

base( std::move( rhs ) ), // base

m ( std::move( rhs.m ) ) // members

{

}

Move ctor

T& operator=( T&& rhs ) {

if( this != &rhs ) {

m = std::move( rhs.m ); // eviscerate

}

return *this;

}

Move assignment

recommended idioms perfect forwarding
Recommended Idioms: Perfect Forwarding

template< typename A, typename B >

T( A&& a, B&& b ) : // binds to any 2 params

ma( std::forward<A>( a ) ),

mb( std::forward<B>( b ) )

{

}

Ctor

template< typename A >

void SetA( A&& a ) // binds to anything

{

ma = std::forward<A>( a );

}

Setter

compilers and move support
Compilers and Move Support

Exhaustive list: http://wiki.apache.org/stdcxx/C++0xCompilerSupport

high level takeaways
High Level Takeaways
  • By overloading on rvalue references, you can branch at compile time on the condition that x is moveable (a temporary object) or not
  • You can implement the overloading gradually
  • Benefits accrue to deep objects
  • Performance improvements can be significant
further research topics i didn t cover
Further Research: Topics I Didn’t Cover
  • xvalues, glvalues, prvalues
  • Emplacement (e.g. “placement insertion”)
    • Create element within container, w/ no moves/copies
    • Uses perfect forwarding and variadic functions
  • Other scenarios where moving lvalues is OK
  • Moves and exceptions
  • Perfect forwarding not always so perfect
    • e.g. integral and pointer types; bitfields, too
  • noexcept and implicit move
best practices
Best Practices
  • Update to compilers that support rvalue references
  • Return by value is now reasonable – both readable and fast
  • Add move ctor/assignment/setters to deep objects
  • Move idiom: this = rhs pointers, rhs pointers = null
  • Use non-constrvalue references
  • When moving, satisfy moved-from obj invariants
  • Avoid return by const T – prevents move semantics
  • Be explicit about implicit special functions
  • Step thru new move code to ensure correctness
thanks
Thanks!
  • Contact me: pkisensee@msn.com
  • Slides: http://www.tantalon.com/pete.htm
  • Scott Meyers: http://www.aristeia.com
  • Stephan Lavavej: http://blogs.msdn.com
  • Dave Abrahams: http://cpp-next.com
  • Thomas Becker: http://thbecker.net
  • Marc Gregoire: http://www.nuonsoft.com
  • Let me know what kind of results you see when you move enable your code
c standard references
C++ Standard References
  • N1610 (v0.1) 2004 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1610.html
  • N2118 (v1.0) 2006 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html
  • N2844 (v2.0) 2009 (VC10 impl) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2844.html
  • N3310 (sections 840, 847, 858) (v2.1) 2011 (VC11 impl) http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html
  • N3053 (v3.0) 2010 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html
r value references
rvalueReferences
  • Scott Meyers’ Move Semantics and rvalueReferences: http://www.aristeia.com/TalkNotes/ACCU2011_MoveSemantics.pdf and http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references
  • Scott Meyers’ Adventures in Perfect Forwarding (C++ and Beyond 2011)
  • Thomas Becker’s rvalueReferences Explained: http://thbecker.net/articles/rvalue_references/section_01.html
  • STL’s blog: http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx?PageIndex=3
  • Marc Gregoire’s Blog http://www.nuonsoft.com/blog/2009/06/07/the-move-constructor-in-visual-c-2010/
  • C++11 Features in Visual Studio C++ 11 http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
  • Mikael Kilpelainen’sLvalues and Rvalueshttp://accu.org/index.php/journals/227
  • Moving from lvalueshttp://cpp-next.com/archive/2009/09/move-it-with-rvalue-references
  • Binary operators http://cpp-next.com/archive/2009/09/making-your-next-move/
  • Emplacement http://stackoverflow.com/questions/4303513/push-back-vs-emplace-back