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 

performance tradeoffs of variable declarations

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Shailesh Humbad
Guest





PostPosted: Thu Oct 30, 2003 7:10 pm    Post subject: performance tradeoffs of variable declarations Reply with quote



I was wondering if there is any performance or executable size
difference between declaring a single temporary variable at the
beginning of a function, versus declaring multiple instances of it where
needed within the function?

Consider this pattern, common in Windows programming:

windowprocedure(message) {
long lTemp;

switch(message) {
case MESSAGE_A:
lTemp++;
return 0;
case MESSAGE_B:
lTemp--;
return 0;
}
}

And compare it with this pattern:


windowprocedure(message) {
switch(message) {
case MESSAGE_A:
long lTempMessageA;
lTempMessageA++;
return 0;
case MESSAGE_B:
long lTempMessageB;
lTempMessageB--;
return 0;
}
}

In both cases, the long is only used temporarily. In the first example,
it is declared with a generic name once at the beginning, and in the
second example, it is declared twice with specific names only where
needed. A smart compiler could optimize the second example by using a
single long in the function signature. Do compilers typically optimize
this way, or will there be a performance cost in exchange for the
improved readability?

Regards,
Shailesh

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





PostPosted: Sat Nov 01, 2003 10:01 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote



"Shailesh Humbad" <humbads1 (AT) hotmail (DOT) com> wrote

Quote:
I was wondering if there is any performance or executable size
difference between declaring a single temporary variable at the
beginning of a function, versus declaring multiple instances of it where
needed within the function?
.....
In both cases, the long is only used temporarily. In the first example,
it is declared with a generic name once at the beginning, and in the
second example, it is declared twice with specific names only where
needed. A smart compiler could optimize the second example by using a
single long in the function signature. Do compilers typically optimize
this way, or will there be a performance cost in exchange for the
improved readability?

Yes, compilers are very good at optimizing this.

Multiple local variables within sub-blocks of a function
are actually often better optimized that a single variable
used in multiple contexts. Since individual local variables are
also better for code maintainable, they definitely should
be preferred.
(also, local variables can then more often be qualified as
'const', which further contributes to performance and
maintainability).


An exception to this guideline might be classes that have an
expensive constructor/destructor. Sharing a single object
instance may avoid the overhead of multiple instance creations,
and makes sense when it doesn't impact code readability.


hth,
Ivan
--
http://ivan.vecerina.com



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

Back to top
Antoun Kanawati
Guest





PostPosted: Sat Nov 01, 2003 10:02 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote



Shailesh Humbad wrote:
Quote:
I was wondering if there is any performance or executable size
difference between declaring a single temporary variable at the
beginning of a function, versus declaring multiple instances of it where
needed within the function?

For builtin types, I have observed, by examining assembly code, that
compilers may lift variables out of their scope.

In general, automatic variable allocation is a static operation: compute
a constant to be added to/subtracted from the stack pointer.

When you turn on optimizations, the compiler may aggregate your stack
needs into a single adjustment.

So, it eventally boils down to the frequency of constructor/destructor
calls, and their occurrence order within the general flow.

For builtin types (int, char, ...), destructors and default constructors
are trivial (do nothing). Otherwise, the primary cost is a load/store
to place the initial values where they're expected.

For types with non-trivial constructors/destructors, on paths where the
temporaries are not used, there is a cost to lifting the temporary
to a higher scope.

Also, if an object survives longer than needed, it may put pressure
on the program resources; for example: a very long string outliving
its use, may cause memory allocation errors, or: A number of small
strings (outliving their use), and a deep call stack may cause
excessive heap fragmentation.

However, putting the temporaries as close to their use as possible
will increase the number of constructor/destructor call sites, thereby
increasing the size of the code. Depending the number of arguments,
their types, and the work required to match them to the parameter
types, this overhead will vary.

Again, some compilers may optimize such situations, e.g.:

if (.....) {
A a(arglist);
.....
}
else {
A a(samearglist);
....
}

could become:
{ A a(arglist); if (....) { ....} else { .... } }
[even if the names of the A objects are different; the real
name of an automatic variable is an offset from the frame
pointer.]

In conclusion: if this is a significant concern, you will need
to look at what happens in the particular situation under analysis.
There is no general answer.

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

Back to top
Christoph Schulz
Guest





PostPosted: Sat Nov 01, 2003 4:34 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

Hello!

Antoun Kanawati <anotunk (AT) comcast (DOT) net> wrote:

Quote:
Again, some compilers may optimize such situations, e.g.:

if (.....) {
A a(arglist);
.....
}
else {
A a(samearglist);
....
}

could become:
{ A a(arglist); if (....) { ....} else { .... } }

This is obviously only true if
- the constructor of A doesn't throw or
- the if-condition doesn't throw.

If both may throw, the optimization could lead to code where A::A throws
even before the condition has been examined. If the condition expression
threw an exception (in the unoptimized code), the optimized code would
not be conforming.

Regards,
Christoph



[ 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: Sat Nov 01, 2003 4:42 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

[email]humbads1 (AT) hotmail (DOT) com[/email] (Shailesh Humbad) wrote (abridged):
Quote:
I was wondering if there is any performance or executable size
difference between declaring a single temporary variable at the
beginning of a function, versus declaring multiple instances of it
where needed within the function?
[...]
windowprocedure(message) {
switch(message) {
case MESSAGE_A:
long lTempMessageA;
lTempMessageA++;
return 0;
case MESSAGE_B:
long lTempMessageB;
lTempMessageB--;
return 0;
}
}

Switch is a bad example because it is one big scope. Thus in the
second case, lTempMessageA and lTempMessageB are both in scope.
Also, you are using lTempMessageA without initialising it and if
you do initialise it, you get an error about that initialisation
being skipped by the second case.

If you use "if" instead:

void test( bool f ) {
if (f) {
int x = 0;
cout << &x;
}
else {
int y = 1;
cout << &y;
}

}

x and y are now in disjoint scopes, and you will very likely find
the compiler gives them the same physical address.

-- 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
Siemel Naran
Guest





PostPosted: Sun Nov 02, 2003 4:49 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

"Dave Harris" <brangdon (AT) cix (DOT) co.uk> wrote in message
Quote:
humbads1 (AT) hotmail (DOT) com (Shailesh Humbad) wrote (abridged):

switch(message) {
case MESSAGE_A:
long lTempMessageA;
lTempMessageA++;
return 0;
case MESSAGE_B:
long lTempMessageB;
lTempMessageB--;
return 0;
}

Switch is a bad example because it is one big scope. Thus in the
second case, lTempMessageA and lTempMessageB are both in scope.

I usually use braces when my case block declares one or more local
variables,

switch(message) {
case MESSAGE_A:
{
long lTempMessageA;
lTempMessageA++;
return 0;
}
case MESSAGE_B:
{
long lTempMessageB;
lTempMessageB--;
}


Quote:
If you use "if" instead:

x and y are now in disjoint scopes, and you will very likely find
the compiler gives them the same physical address.

Probably using switch with braces yields the same behavior.

--
+++++++++++
Siemel Naran


[ 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 Nov 03, 2003 10:25 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

You also have to consider enregistration, where a compiler makes the
variable live entirely inside of a register; this is a plus because it
avoids expensive memory loads and stores, and doesn't need to adjust
the local stack pointer. Built-in types and simple structs should be
easily enregistered, C++ classes may not be if they don't have trivial
constructors/destructors.

In the face of enregistration, you get register usage pressure, where
if the compiler cannot figure out how to shorten the lifetime of a
variable, it prevents the register from being reused in other code.
CPU rename registers will help here.

The practical effect on coding is: if you think a variable is likely
to be enregistered, make its scope as small as possible.

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
John Potter
Guest





PostPosted: Tue Nov 04, 2003 1:12 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

On 1 Nov 2003 05:02:45 -0500, Antoun Kanawati <anotunk (AT) comcast (DOT) net>
wrote:

Quote:
Again, some compilers may optimize such situations, e.g.:

if (.....) {
A a(arglist);
.....
}
else {
A a(samearglist);
....
}

could become:
{ A a(arglist); if (....) { ....} else { .... } }

Do you know of any compiler that does that?

struct S {
S () { ++ c; }
static int c;
};
int S::c(0);
int main () {
if (S::c) {
S s;
cout << S::c;
}
else {
S s;
cout << - S::c;
}
cout << "n";
}

Does it get this right? Moving objects with c/dtors takes a lot
more work than moving builtins.

John

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

Back to top
Shailesh Humbad
Guest





PostPosted: Tue Nov 04, 2003 9:37 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

Antoun Kanawati wrote:

Quote:
In general, automatic variable allocation is a static operation:
compute a constant to be added to/subtracted from the stack pointer.

When you turn on optimizations, the compiler may aggregate your stack
needs into a single adjustment.

From this, I take it that the amount of stack space to add/subtract is
calculated at compile time. However, the addition/subtraction itself
must be done at run-time, based on the case being processed. I assume
then, that these add/sub instructions would increase the code size. My
main switch statement has nearly 150 cases, so that would add up.
(I wonder how variable stack size affects branch prediction?)

On the other hand, if the stack space is aggregated to one value by the
compiler, then there is no code size increase, but each time the
function is called, a larger than necessary chunk of stack space will
have to be allocated. In either case, for an ordinary window procedure,
the effect will likely not be significant compared to the other
processing that has to be done.

I analyzed the disassembly of the simple code below using the VC7
compiler in VS.Net, with default Release build optimizations:

int i;
i = rand() % 2;
switch(i)
{
case 0:
int iTempA;
iTempA = rand() + 1;
return iTempA;
case 1:
int iTempB;
iTempB = rand() - 1;
return iTempB;
}

There was no difference between declaring a single variable, iTemp,
versus declaring two case-local variables, iTempA and iTempB. There was
also no difference when braces were put around the cases. In all cases,
the EAX register is used, which is highly optimized. I understand that
this example probably can not be generalized, as stated by several posters.

Dave Harris wrote:

Quote:
Switch is a bad example because it is one big scope. Thus in the
second case, lTempMessageA and lTempMessageB are both in scope. Also,
you are using lTempMessageA without initialising it and if you do
initialise it, you get an error about that initialisation being
skipped by the second case.

I've never seen a compiler that warned about missing initializations
within the scope of an unbraced case. Does this come from lint or some
other code checker?

My approach has been to initialize most objects with non-trivial
constructors once, and these objects stay around throughout the lifetime
of the program. I have not been using any case-level temporary
variables of instrinsic type, but instead a handful of function-level
temporaries instead.

This forcibly optimizes the required stack space, but the tradeoff is
that I use the same variable in a hundred different places, with
different meanings each time. I would much rather have the compiler do
optimization. It looks like the compiler can do this, and even if it
can't, the penalty is not so great, so I'll update my coding practice.

Thanks to all who replied.

Shailesh


FYI: Here's a description of the .Net garbage collector algorithm, which
makes some interesting assumptions about when and how software allocates
memory.

http://msdn.microsoft.com/library/en-us/cpguide/html/cpconautomaticmemorymanagement.asp

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

Back to top
Ben Hutchings
Guest





PostPosted: Tue Nov 04, 2003 5:21 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

John Potter wrote:
Quote:
On 1 Nov 2003 05:02:45 -0500, Antoun Kanawati wrote:

Again, some compilers may optimize such situations, e.g.:

if (.....) {
A a(arglist);
.....
}
else {
A a(samearglist);
....
}

could become:
{ A a(arglist); if (....) { ....} else { .... } }

Do you know of any compiler that does that?

VC++ 7.1 can do this, as you can see in the following source code
and corresponding assembly code:

1: int one(int x, int y);
2: int two(int x, int y);
3:
4: int main()
5: {
6: if (one(2, 3))
00401000 push 3
00401002 push 2
00401004 call one (401030h)
00401009 add esp,8
0040100C test eax,eax
7: one(4, 5);
0040100E push 5
00401010 push 4
00401012 je main+1Fh (40101Fh)
00401014 call one (401030h)
8: else
9: two(4, 5);
00401019 add esp,8
10: }
0040101C xor eax,eax
0040101E ret
8: else
9: two(4, 5);
0040101F call two (401040h)
00401024 add esp,8
10: }
00401027 xor eax,eax
00401029 ret

Quote:
struct S {
S () { ++ c; }
static int c;
};
int S::c(0);
int main () {
if (S::c) {
S s;
cout << S::c;
}
else {
S s;
cout << - S::c;
}
cout << "n";
}

Does it get this right? Moving objects with c/dtors takes a lot
more work than moving builtins.

Yes, it generates the test before the increment (compiled inline).
Interestingly, it merges the calls to std::cout.operator<< as if
the if...else statement was replaced with:

int result;
if (S::c) {
S s;
result = S::c;
}
else {
S s;
result = - S::c;
}
cout << result;

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

Back to top
Antoun Kanawati
Guest





PostPosted: Tue Nov 04, 2003 8:11 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

John Potter wrote:
Quote:
On 1 Nov 2003 05:02:45 -0500, Antoun Kanawati wrote:


Again, some compilers may optimize such situations, e.g.:

if (.....) {
A a(arglist);
.....
}
else {
A a(samearglist);
....
}

could become:
{ A a(arglist); if (....) { ....} else { .... } }


Do you know of any compiler that does that?

struct S {
S () { ++ c; }
static int c;
};
int S::c(0);
int main () {
if (S::c) {
S s;
cout << S::c;
}
else {
S s;
cout << - S::c;
}
cout << "n";
}

Does it get this right? Moving objects with c/dtors takes a lot
more work than moving builtins.

I've seen done with builtins, and I don't think PODs are that
different either, conceptually or algorithmically.

For non-PODs, it is a more difficult problem, but still generally
approachable. Code optimization is a well established discipline
dating back to the old days of FORTRAN. In the case above, a
simple static analysis of the code will prove that the particular
optimization under consideration changes the semantics of the program,
and is therefore unsafe.

That is why I said "may" not "will" or "shall".
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQE/p9y7JJVk0d/xqeYRAvvsAKDWv0kHCvcZvC3uROV8Ci5FLvaCPgCglb6i
NwSgxmdRQHrAcasWUtWeXPE=
=EXXy
-----END PGP SIGNATURE-----

[ 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: Wed Nov 05, 2003 9:59 am    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

[email]replyat (AT) somacon (DOT) com[/email] (Shailesh Humbad) wrote (abridged):
Quote:
Switch is a bad example because it is one big scope. Thus in the
second case, lTempMessageA and lTempMessageB are both in scope. Also,
you are using lTempMessageA without initialising it and if you do
initialise it, you get an error about that initialisation being
skipped by the second case.

I've never seen a compiler that warned about missing initializations
within the scope of an unbraced case. Does this come from lint or
some other code checker?

Not missing; skipped. The error comes from the compiler itself. Try
typing some code into Comeau's online compiler, at:
http://www.comeaucomputing.com/tryitout/

For example:

void demo( int x ) {
switch (x) {
case 0:
int y = 1;
break;
case 1:
++y;
break;
}
}

The key feature is the "case 1:", which allows the control flow to
reach y's destructor without ever executing its constructor. Comeau
reports one error. This is required by the C++ ANSI standard.

If you omit the "= 1" the code should compile, but will yield
undefined behaviour if executed. No diagnostic is required, but a
good compiler (such as Comeau's) will warn about it anyway.

-- 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
Nick Hounsome
Guest





PostPosted: Sat Nov 08, 2003 1:53 pm    Post subject: Re: performance tradeoffs of variable declarations Reply with quote

Quote:
On the other hand, if the stack space is aggregated to one value by the
compiler, then there is no code size increase, but each time the
function is called, a larger than necessary chunk of stack space will
have to be allocated.

No - the size allocated is not the sum of all the branch sizes it is just
the biggest
thus you only waste stack if you take a branch/switch with smaller than max
stack
requirement.

e.g. if( a ) { int x; ..... } else { char b[100]; ..... } will reserve
100 bytes of stack
not 100+sizeof(int)

A lot of the work in calculating minimum stack requirement this is probably
also relevant to determining
optimal register usage and so is pretty much gauranteed for any compiler
doing any
sort of half decent optimization.



[ 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
Page 1 of 1

 
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.