C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Efficiency of operator= using new swap idiom versus old scho
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Roshan Naik
Guest





PostPosted: Sat Aug 21, 2004 4:05 am    Post subject: Efficiency of operator= using new swap idiom versus old scho Reply with quote



I did some performance measurements on the operator = ( ) implemented
using the two idioms..


1) Test for self assignment and and then assign the members individually
.... the old idiom
A::operator = ( const A& other ) {
if( this != &other) { // i know this test may be
simplistic sometimes..but never mind for now
m1 = other.m1 ; // no-throw guarantee
expected
m2 = other.m2 ; // no-throw guarantee
expected
}
return *this;
}

Versus..
2) Copy construct a temporary and swap the members...which is the newer
idiom

A& A::operator = ( A other ) {
A::Swap( other) ;
return *this;
}
void A::Swap ( A& other ) {
std::swap(m1, other.m1) ; // no-throw guarantee
expected
std::swap(m2, other.m2) ; // no-throw guarantee
expected
}


I did measurements (using VC++ 2003) for both cases,... self assignments
and non-self assignments.
To make a long story short: Under debug build the older idiom
outperformed (more like smoked the
daylights out of) the newer idiom. Under Release mode they both showed
no performance diff.

I see that the new swap idiom doesn't add any greater exception safety
than the older idiom.
To be non-throwing, std:::swap requires two guarantees from the types it
is used upon, viz.
- Copy constructor should not throw (std::swap will create a
temporary copy of one of its args )
- Assignment Operator should not throw ( std::swap will use
assignment to exchange values )

The older idiom simply requires one guarantee to be exception safe.
- Assignment Operator should not throw


Now ofcourse we may feel that the newer idiom may deliver better
performance for
'non-self assignments' due to the lack of self test. But then a lot more
cost is
incurred due to the of creation of a lot of unnecessary temporaries.

In the swap idiom, notice that a copy of each data member of the right
hand side of the assignment is
made twice! Once when passing by value to A::operator= and again by
std::swap inside A::Swap.

Measurements for non optimized builds confirm this. But the measurements
in the optimized builds
which show equal performance are what puzzle me.

What on earth happened to all that extra overhead ? What kind of
optimizations could have really
dampned that overhead ? . "Observable Behavior" related stuff which
turned all the code into NOP's
may be?

Anycase: The new idiom doesn't seem to be superior in anyway to the old
one. First the performance
measurements don't show any difference. Second the newer idiom requires
more no-throw guarantees
than the older one.

The other issue is a philosophical one. Assignment is a more primitive
operation than swap.
And the newer idiom implements a more primitive operation in terms of a
less primitive one.
(and requiring more guarantees in the process).

For sake of completeness I guess I should add... the swap idiom *may*
not necessarily perform as fast as the old idiom on all compilers
....(even at full optimization level) as optimizations are compiler
dependent.

Here's my questions:
- So do we really gain anything from the newer idiom ?
- Less important: Any clue (off the top of your head) what
optimizations the compiler could be doing here ?


PS: I will shortly post my measurements and original code ...once I get
back to the machine where I ran the test.

--Roshan



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Roshan Naik
Guest





PostPosted: Sat Aug 21, 2004 10:22 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote





Roshan Naik wrote:

Quote:

PS: I will shortly post my measurements and original code ...once I get
back to the machine where I ran the test.


Here are the results of the test .....

[Debug Build]
Self Assignment, swap idiom: 18sec
Non-Self Assignment, swap idiom: 17sec
Self Assignment, non-swap idiom: 2sec
Non-Self Assignment, swap idiom: 2sec

[Release Build]
Self Assignment, swap idiom: 0sec
Non-Self Assignment, swap idiom: 0sec
Self Assignment, non-swap idiom: 0sec
Non-Self Assignment, swap idiom: 0sec


Here is the code.......




#include <iostream>
#include <utility>
#include<ctime>


// non POD data type
struct X {
int a[2];
virtual ~X(){}
};


using namespace std;

// This class implements operator= using swap idiom
struct A {
int m1;
double* m2;
X m3;

A() {}

A(const A& other) {
m1=other.m1;
m2=other.m2;
m3=other.m3;
}

A& operator = (A rhs) {
Swap(rhs);
return *this;
}

void Swap(A& rhs) {
swap( m1, rhs.m1 );
swap( m2, rhs.m2 );
swap( m3, rhs.m3 );
}
};

// This class implements operator= WITHOUT using swap
// otherwise identical to class A above
struct B {
int m1;
double* m2;
X m3;

B() {}

B& operator = (const B& rhs) {
if(this==&rhs)
return *this;
m1=rhs.m1;
m2=rhs.m2;
m3=rhs.m3;
return *this;
}
};


int main()
{
const long long count= 1000*1000*10;
A a1, a2;

time_t start, end;

// declared volatile to ensure optimzer doesnt nuke the loops later
volatile long long i,j,k,l,m;

// self assignment ..swap idiom
//------------------------------
time(&start);
for (j = 0; j < count ; ++j ) { a1=a1;}
time(&end);
// printing counter variable 'j' to ensure that loop actually excuted
// count times... just incase the optimzer decided the loop was worthless!
cout << "A::self " << end - start << " " << j << "n";

// non-self assignment ..swap idiom
//------------------------------
time(&start);
for (k = 0; k < count ; ++k ) { a1=a2; }
time(&end);
cout << "A::other " << end - start << " " << k << "n";

// self assignment ..non-swap idiom
//------------------------------
B b1,b2;
time(&start);
for (l= 0; l < count ; ++l ) { b1=b1; }
time(&end);
cout << "B::self " << end - start << " " << l << "n";

// non-self assignment ..non-swap idiom
//------------------------------
time(&start);
for (m= 0; m < count ; ++m ) { b1=b2; }
time(&end);
cout << "B::other " << end - start << " " << m << "n";

return 0;
}








[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Sat Aug 21, 2004 4:02 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote



Roshan Naik <someone (AT) nowhere (DOT) com> writes:

Quote:
I did some performance measurements on the operator = ( ) implemented
using the two idioms..


1) Test for self assignment and and then assign the members individually
... the old idiom
A::operator = ( const A& other ) {
if( this != &other) { // i know this test may be
simplistic sometimes..but never mind for now
m1 = other.m1 ; // no-throw guarantee expected
m2 = other.m2 ; // no-throw guarantee expected
}
return *this;
}

Versus..

2) Copy construct a temporary and swap the members...which is the newer
idiom

A& A::operator = ( A other ) {
A::Swap( other) ;
return *this;
}
void A::Swap ( A& other ) {
insert: using std::swap;
std::swap(m1, other.m1) ; // no-throw guarantee expected
remove----------------^^^^^
std::swap(m2, other.m2) ; // no-throw guarantee expected
remove----------------^^^^^
}


I did measurements (using VC++ 2003) for both cases,... self
assignments and non-self assignments. To make a long story short:
Under debug build the older idiom outperformed (more like smoked the
daylights out of) the newer idiom. Under Release mode they both
showed no performance diff.

There are quite a few misonceptions here...

first, the std:: qualifications on swap above don't belong. The
presumption is that the types that might be members of A have a fast
(constant time), nonthrowing swap, like the one for std::vector, that
can be found via argument dependent lookup (ADL).

Quote:
I see that the new swap idiom doesn't add any greater exception
safety than the older idiom.

That's wrong. But it's not to say I advocate using swap for
assignment; I don't.

Quote:
To be non-throwing, std:::swap requires two guarantees from the
types it is used upon, viz.

- Copy constructor should not throw (std::swap will create a
temporary copy of one of its args )
- Assignment Operator should not throw ( std::swap will use
assignment to exchange values )

Not if the items being swapped are std:: containers, or strings. And
_unqualified_ swap can often be implemented more efficiently than the
default std::swap implementation (the one that takes effect for types
not in std:: or without a swap overload), by using the same
techniques as the std:: containers' swap. That doesn't rely on
whole-object assignment.

Quote:
The older idiom simply requires one guarantee to be exception safe.

- Assignment Operator should not throw

That's what it requires to be atomic or nothrow. Whether or not it's
required for "exception safety" depends on A's invariants.

Quote:
Now ofcourse we may feel that the newer idiom may deliver better
performance for 'non-self assignments' due to the lack of self
test. But then a lot more cost is incurred due to the of creation of
a lot of unnecessary temporaries.

In the swap idiom, notice that a copy of each data member of the
right hand side of the assignment is made twice! Once when passing
by value to A::operator= and again by std::swap inside A::Swap.

Measurements for non optimized builds confirm this. But the
measurements in the optimized builds which show equal performance
are what puzzle me.

What on earth happened to all that extra overhead ? What kind of
optimizations could have really dampned that overhead ? .
"Observable Behavior" related stuff which turned all the code into
NOP's may be?

Anycase: The new idiom doesn't seem to be superior in anyway to the
old one. First the performance measurements don't show any
difference. Second the newer idiom requires more no-throw guarantees
than the older one.

The "new idiom" if you can call it that, actually has performance
problems in larger systems. Consider that std::vector may be able to
do assignment with no new allocations. If you do it with copy/swap,
you force a new buffer to be allocated.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Thorsten Ottosen
Guest





PostPosted: Sat Aug 21, 2004 4:04 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

"Roshan Naik" <someone (AT) nowhere (DOT) com> wrote


Quote:
Anycase: The new idiom doesn't seem to be superior in anyway to the old
one. First the performance
measurements don't show any difference. Second the newer idiom requires
more no-throw guarantees
than the older one.

Giving operator=() the strong exception-safety guarantee is often wrong, in particular if there is a performance difference between
the two approaches. The reason is
that if you want commit and roll-back semantics, it is usually on a higher level than a signle assignment statement.

br

Thorsten



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daniel R. James
Guest





PostPosted: Sat Aug 21, 2004 4:05 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

Roshan Naik <someone (AT) nowhere (DOT) com> wrote

Quote:
I see that the new swap idiom doesn't add any greater exception safety
than the older idiom.
To be non-throwing, std:::swap requires two guarantees from the types it
is used upon, viz.
- Copy constructor should not throw (std::swap will create a
temporary copy of one of its args )
- Assignment Operator should not throw ( std::swap will use
assignment to exchange values )

The older idiom simply requires one guarantee to be exception safe.
- Assignment Operator should not throw

The swap idiom isn't intended to provide the no throw guarantee, it's
used for the strong guarantee - basically, that if an exception throws
the object will not be changed. To get this you only need a no throw
swap, the copy constructor only requires the basic guarantee.

But the question is: is the strong guarantee needed for copy
constructors?

In your benchmarks, you seem to be doing fairly simple copies, the
perfomance difference really comes for more complicated classes where
resources (such as memory) are allocated. The swap idiom requires you
to allocate a new resource, which can be expensive, in a traditional
copy constructor you might be able to reuse the exisiting resource.

Daniel

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Balog Pal
Guest





PostPosted: Sun Aug 22, 2004 3:06 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

"Roshan Naik" <someone (AT) nowhere (DOT) com> wrote


Quote:
[Release Build]
Self Assignment, swap idiom: 0sec
Non-Self Assignment, swap idiom: 0sec
Self Assignment, non-swap idiom: 0sec
Non-Self Assignment, swap idiom: 0sec

With 99.5% probability your test measured how fast you can count your
counter, and the optimizer replaced all your assignments with a single one.
Try some better measuring suite. And never forget to look at the assy the
compiler created to see your stuff is indeed there in the first place.

As to the original question: the purpose of the full thing is exactly to
deal with exceptions that can hit on member assignment. You can expect a
nonthrowing swap but can not expect a nonthrowing op=.
Whenever your premise of

Quote:
m1 = other.m1 ; // no-throw guarantee expected

holds the swap thing is really not needed.

Try your example when your A has three vectors as members...

Paul



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David B. Held
Guest





PostPosted: Sun Aug 22, 2004 3:11 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

Roshan Naik wrote:
Quote:
[...]
I see that the new swap idiom doesn't add any greater exception safety
than the older idiom.

Is that so??

Quote:
To be non-throwing, std:::swap requires two guarantees from the types it
is used upon, viz.

Where can I find this function "std:::swap"? ;>

Quote:
- Copy constructor should not throw (std::swap will create a
temporary copy of one of its args )

Since std::swap() should only be called on POD types and types that
aren't "tricky" (have internal dynamic allocation, etc.), this isn't
a problem.

Quote:
- Assignment Operator should not throw ( std::swap will use
assignment to exchange values )

Same as above.

Quote:
The older idiom simply requires one guarantee to be exception safe.
- Assignment Operator should not throw

And here is where there is some confusion. You have multiple levels
at which swap() is called. There is the level at which the operator=()
in question is being defined, and then there are the nested swap()s
that may be called inside this high-level swap(). The non-throwing
copy c'tor and assignment operator constraints above only apply to
these member types. It does not apply to the top-level object itself.
Take, for instance:

foo& operator=(foo rhs)
{
swap(rhs);
return *this;
}

void swap(foo& rhs)
{
std::swap(s1_, rhs.s1_);
std::swap(s2_, rhs.s2_);
}
private:
std::string s1_;
std::string s2_;

Now, compare this to "old school":

foo& operator=(foo const& rhs)
{
s1_ = rhs.s1_;
s2_ = rhs.s2_;
return *this;
}

(I'm neglecting the self-assignment issue, because it isn't really
relevant to the exception-safety issue). Can you begin to see what is
going on yet? In the old-school version, we require
std::string::operator= to be no-throw!! However, we impose no such
requirement in the new idiom, because it never calls operator=() on a
type that might throw. It only calls swap() on types that might throw
on copy/assignment. It only calls operator=() way down inside the call
to std::swap() which swaps the actual string pointers (if, in fact, it
is implemented with the same protocol). And at that point, you
trivially get the no-throw guarantee. Furthermore, we are not requiring
that std::string even have a no-throw copy c'tor! If the copy c'tor
throws, then operator=() has no effect. So in reality, to get even
the *basic* guarantee out of the old-school method (sans try/catch
blocks or some other explicit exception handling mechanism), you need
to impose the rather draconian requirement that each member type have a
non-throwing assignment operator! In the new idiom, you only need to
have a non-throwing swap, which tends to be rather easy to provide and
can be useful in other situations as well.

Quote:
[...]
In the swap idiom, notice that a copy of each data member of the right
hand side of the assignment is made twice! Once when passing by value
to A::operator= and again by std::swap inside A::Swap.

Nobody said it was free, which is exactly why you should only use it
when you need it.

Quote:
Measurements for non optimized builds confirm this. But the measurements
in the optimized builds
which show equal performance are what puzzle me.

Imagine that the compiler sees a sequence of assignments roughly like
so, where rhs is the source, tmp is the copy, and swap is the local
temp inside swap() (obviously, swap can be inlined in a good deal of
the cases):

tmp.x = rhs.x;
swap.x = tmp.x;
tmp.x = this.x;
this.x = swap.x;

The compiler can see that the second assignment can be transformed like
so:

tmp.x = rhs.x;
swap.x = rhs.x;
tmp.x = this.x;
this.x = swap.x;

Then it can see that the first assignment has no effect, and can be
eliminated:

swap.x = rhs.x;
tmp.x = this.x;
this.x = swap.x;

It can again see that the third assignment can be replaced like so:

swap.x = rhs.x;
tmp.x = this.x;
this.x = rhs.x;

And now the first assignment has no effect:

tmp.x = this.x;
this.x = rhs.x;

Since tmp.x does not get read after the swap() and before it is
destroyed, the first assignment has no effect, and can thus be
eliminated:

this.x = rhs.x;

Quote:
What on earth happened to all that extra overhead ? What kind of
optimizations could have really dampned that overhead ? . "Observable
Behavior" related stuff which turned all the code into NOP's
may be?

Well, the analysis above assumes POD types which have trivial
assignment. I'm not sure the compiler can optimize away all the
assignments when operator=() is user-defined.

Quote:
Anycase: The new idiom doesn't seem to be superior in anyway to the old
one.

I hope I've convinced you otherwise...

Quote:
[...]
Second the newer idiom requires more no-throw guarantees than the older
one.

....and that this is patently false.

Quote:
The other issue is a philosophical one. Assignment is a more primitive
operation than swap.
[...]

I'm not sure how you can say that when we are clearly implementing
assignment in terms of swap. The only reason we must implement swap
in terms of assignment is because C++ doesn't give us a fundamental
swap operator (though many hardware platforms do). Some would say that
copy, swap, and move are all more fundamental than assignment, and I
would tend to agree with that view. Why? Because assignment entails
destruction, whilst the others do not. That happens to be why
assignment is more complicated than copy (and thus why assignment can
be implemented with copy).

Dave

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dave Harris
Guest





PostPosted: Sun Aug 22, 2004 3:16 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

[email]someone (AT) nowhere (DOT) com[/email] (Roshan Naik) wrote (abridged):
Quote:
I did some performance measurements on the operator = ( ) implemented
using the two idioms..

1) Test for self assignment and and then assign the members individually
... the old idiom
Versus..
2) Copy construct a temporary and swap the members...which is the newer
idiom
[...]
To make a long story short: Under debug build the older idiom
outperformed (more like smoked the daylights out of) the newer
idiom. Under Release mode they both showed no performance diff.

I guess the new idiom was able to exploit instruction-level parallelism in
the CPU.


Quote:
I see that the new swap idiom doesn't add any greater exception safety
than the older idiom.

The new idiom is only preferred if it does add greater exception safety,
and not always then. Specifically, the new idiom ensures the object's
state remains well-defined even if the assignment fails. In your example
the assignment cannot fail, so there's no advantage to using the new
idiom.

To see the advantage, consider a case like:

struct A {
std::string s1;
std::string s2;
A &operator=( const A &rhs ) {
if (this == &rhs)
return *this;
s1 = rhs.s1;
s2 = rhs.s2; // May throw.
return *this;
}
};

The commented line may throw because the right-hand-side string may be
longer than the old and the assignment may need to allocate more memory,
which may not be available. If it does throw, then we s1 from the one
object and s2 from the other, which may not make sense.

Using:
A &operator=( const A &rhs ) {
std::string rhs_s1( rhs.s1 ); // May throw.
std::string rhs_s2( rhs.s2 ); // May throw.
rhs_s1.swap( s1 ); // May not throw.
rhs_s2.swap( s2 ); // May not throw.
return *this;
}

the assignment may still fail, but if it does it leaves the target object
in a well-defined, indeed unchanged, state.

-- Dave Harris, Nottingham, UK

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Sun Aug 22, 2004 10:48 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

Quote:
But then a lot more
cost is
incurred due to the of creation of a lot of unnecessary temporaries.

Depends. If the object is small or totally stack resident, then the
temporary only consumes stack space or can even be enregistered, so
the cost may be negligible.

Quote:
In the swap idiom, notice that a copy of each data member of the right
hand side of the assignment is
made twice! Once when passing by value to A::operator= and again by
std::swap inside A::Swap.

Note the signature of A::operator= is wrong, it should be:

A& A::operator = ( const A& other ) {
A::Swap( other) ;
return *this;
}

Thus there is only one copy not two.

Quote:
Measurements for non optimized builds confirm this. But the measurements
in the optimized builds
which show equal performance are what puzzle me.

What on earth happened to all that extra overhead ? What kind of
optimizations could have really
dampned that overhead ? . "Observable Behavior" related stuff which
turned all the code into NOP's
may be?

See my first para. Also the compiler might notice that the temp object
is eventually destroyed without any exceptions intervening, and thus
it simply substitutes a cleaner assignment.

Quote:
The other issue is a philosophical one. Assignment is a more primitive
operation than swap.
And the newer idiom implements a more primitive operation in terms of a
less primitive one.
(and requiring more guarantees in the process).

Not necessarily. Consider it the law of conservation of matter, as
erm, applied to C++ objects. A swap doesn't destroy the values of any
objects, but an assign implicitly destroys the old value of the
assigned-to object. Thus a swap may not need to invoke a destructor,
but an assign might (implicitly).

Another consideration is duplication of code. An assign and a copy
constructor are philosophically similar; they make copies, but the
former destroys something first, while the second assumes empty space.
Thus it makes sense to define assign == copy construct + swap.

As for exception safety, given the above, some heavy objects will have
a non-trivial copy construct, e.g. they allocate memory or do things
that could throw. If a copy construct were to throw, the object never
existed and we are fine (more or less...). But if an assign were to
throw, we could be in the middle of creating and/or destroying parts
of the object, so we'd be left with a chimera object. Thus defining
assign == copy construct + swap makes the boundaries between creation
and destruction distinct and either you get an all new object or the
old object untouched.

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Sun Aug 22, 2004 10:54 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

"David B. Held" <dheld (AT) codelogicconsulting (DOT) com> writes:

Quote:
So in reality, to get even the *basic* guarantee out of the
old-school method (sans try/catch blocks or some other explicit
exception handling mechanism), you need to impose the rather
draconian requirement that each member type have a non-throwing
assignment operator!

On the face of it, that's incorrect. Whether or not you get the
basic guarantee depends on the class invariants. If it's just an
aggregate of values with no particular required value relationship,
you get the basic guarantee from the compiler-generated operator=.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David B. Held
Guest





PostPosted: Sun Aug 22, 2004 10:57 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

Glen Low wrote:
Quote:
[...]
Note the signature of A::operator= is wrong, it should be:

A& A::operator = ( const A& other ) {
A::Swap( other) ;
return *this;
}

Thus there is only one copy not two.
[...]

And how do you propose to perform a swap on a const&, Glen? ;> Assuming
the compiler let you, are you sure the caller wants your old value in
her object?

Dave

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daryle Walker
Guest





PostPosted: Sun Aug 22, 2004 11:08 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

In article <41268E69.550C51AD (AT) nowhere (DOT) com>,
Roshan Naik <someone (AT) nowhere (DOT) com> wrote:

Quote:
I did some performance measurements on the operator = ( ) implemented
using the two idioms..


1) Test for self assignment and and then assign the members individually
... the old idiom
A::operator = ( const A& other ) {
if( this != &other) { // i know this test may be
simplistic sometimes..but never mind for now
m1 = other.m1 ; // no-throw guarantee
expected
m2 = other.m2 ; // no-throw guarantee
expected
}
return *this;
}

I think you need to update your idiom reading. AFAIK, you do _not_ do
the explicit self-assignment test anymore! You just do the member
assignments directly.

Why? Because self-assignment is so rare, adding an explicit test for it
anti-optimizes the usual different-assignment case.

For insular members, you can just do an assignment directly. For
members that reference a buffer or other resource, you call the resource
duplication routine, do the assignment, and release the unused resource.

Actually, if you have all insular members, the default assignment
routine the compiler synthesizes is OK. It looks like the case here.

Quote:
Versus..
2) Copy construct a temporary and swap the members...which is the newer
idiom

A& A::operator = ( A other ) {
A::Swap( other) ;
return *this;
}
void A::Swap ( A& other ) {
std::swap(m1, other.m1) ; // no-throw guarantee
expected
std::swap(m2, other.m2) ; // no-throw guarantee
expected
}
[TRUNCATE the conclusions]


As another poster said, add an "using std::swap" and remove the "std::"
from the actual swapping calls. That will let ADL take over for types
outside the standard library. (You should be nice and add a non-member
"swap" function for type "A" yourself, to continue the cycle.)

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Mon Aug 23, 2004 10:32 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote

Quote:
[...]
Note the signature of A::operator= is wrong, it should be:

A& A::operator = ( const A& other ) {
A::Swap( other) ;
return *this;
}

Thus there is only one copy not two.
[...]

And how do you propose to perform a swap on a const&, Glen? ;> Assuming
the compiler let you, are you sure the caller wants your old value in
her object?

Oops, *embarrassed look*. I was really thinking of

A& A::operator= (const A& other)
{
A temp (other);
A::Swap (temp);
return *this;
}

But of course the original code is equivalent, and there is still only
one copy *construction* (but swap "redundantly" does do another copy).

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Roshan Naik
Guest





PostPosted: Mon Aug 23, 2004 10:34 am    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote



Balog Pal wrote:
Quote:

With 99.5% probability your test measured how fast you can count your
counter, and the optimizer replaced all your assignments with a single one.
Try some better measuring suite. And never forget to look at the assy the
compiler created to see your stuff is indeed there in the first place.


Ok, possible, but unlikely with 99.5% probability Smile
I did consider the fact that observable behavior issues might optimize
away some of the stuff into oblivion, but I *dont* think most
mainstream compilers (at least vc++ and borland c++ builder with which I
have a modest amount of experience ) are that smart yet.

Anyway for the record, I modified the code ( and its listed below ) to
mark everything relevant that i could spot to volatile.

The results turned out similar but (as expected) slightly worse.

Self Assignment, swap idiom: 1 sec
Non-Self Assignment, swap idiom: 0 sec
Self Assignment, non-swap idiom: 1 sec
Non-Self Assignment, non-swap idiom: 0 sec


....hmm interesting... self assignment is more expensive in both cases !
Ofcourse in these tests the copy construction and assignment
operators are very lightweight...nevertheless its a little unexpected
I can only think of the instruction pipeline being affected in the
non-swap idiom case.





[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Roshan Naik
Guest





PostPosted: Mon Aug 23, 2004 9:58 pm    Post subject: Re: Efficiency of operator= using new swap idiom versus old Reply with quote



Daryle Walker wrote:


Quote:
Roshan Naik <someone (AT) nowhere (DOT) com> wrote:


I did some performance measurements on the operator = ( ) implemented
using the two idioms..


1) Test for self assignment and and then assign the members individually
... the old idiom
A::operator = ( const A& other ) {
if( this != &other) { // i know this test may be
simplistic sometimes..but never mind for now
m1 = other.m1 ; // no-throw guarantee
expected
m2 = other.m2 ; // no-throw guarantee
expected
}
return *this;
}


I think you need to update your idiom reading.


Ok if you insist.
:-D


Quote:
AFAIK, you do _not_ do
the explicit self-assignment test anymore! You just do the member
assignments directly.



My dear friend... did you read what i wrote above that code ..
"the old idiom" ? Well never mind.

Also this thread discussing about the "old idiom
versus the new idiom". The new idiom being the one you are asking
me to update my reading on. :)


Quote:
Why? Because self-assignment is so rare, adding an explicit test for
it anti-optimizes the usual different-assignment case.

Really ? Have you ever measured it ?
If not please look (once again) at the permformace measurements I
have posted along with the code in this thread.






Quote:
[TRUNCATE the conclusions]

As another poster said, add an "using std::swap" and remove the "std::"
from the actual swapping calls. That will let ADL take over for types
outside the standard library. (You should be nice and add a non-member
"swap" function for type "A" yourself, to continue the cycle.)



Look at the code I used to make performance measurements.
Anyway for those measurements (in my code) it doesnt really make
a difference.

-Roshan

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.