Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++ Performance Overheads

3 views
Skip to first unread message

samvit

unread,
Apr 24, 1999, 3:00:00 AM4/24/99
to
I am trying to find out whether C++ programs incur any big performance
penalties in the form of excessive constructor/destructor calls,
operator overloading, function overloading, inheritance and virtual
functions.

In particular, are there any performance issues to take care of, if we
decide to move from C to C++.

Also can anyone suggest some good references for this study.

--
SAMVIT KAUL
Senior Software Engineer
---------------------------------------------------------------
Office : Philips Research Laboratories, Building WAYp 98
Prof. Holstlaan 4, 5656 AA Eindhoven, The Netherlands
Res : Elzentlaan 81, 5611 LK Eindhoven, The Netherlands
E-mail : sam...@natlab.research.philips.com
Phone : +31-40-2114081 (Res) +31-40-2745109 (Off)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Steve Clamage

unread,
Apr 24, 1999, 3:00:00 AM4/24/99
to
samvit <sam...@natlab.research.philips.com> writes:

>I am trying to find out whether C++ programs incur any big performance
>penalties in the form of excessive constructor/destructor calls,
>operator overloading, function overloading, inheritance and virtual
>functions.

At the risk of sounding simplistic, any "overhead" you get is
the overhead you ask for.

A constructor initializes an object. If you didn't initialize an
object in your C code, and don't want to initialize it in your
C++ code, you won't have a constructor call. If you want to
initialize the object, it takes some amount of code to do it.
Except in unusual cases, the constructor be will no less efficient
than any other method. It will be more reliable, since you need
only write the initialization code once, instead of trying to
get it right in each location where the initialization occurs,
and because the compiler will call it automatically for you.
(No more "Oops, I forgot to call the init function" errors.)

A destructor performs user-defined cleanup when an object goes
out of scope or is removed from the heap. If you don't want any
cleanup done, don't write the destructor. If you do want cleanup,
it takes some amount of code to perform the cleanup. Either you
put it in the destructor, which you write once and which thereafter
gets called automatically, or you have to remember to write the
cleanup code explicitly each place an object goes away.

Function overloading has no runtime overhead of any kind. C++
allows you to use one name to refer to more than one function.
Overloading has no other significance, and no run-time implications.

When you ask about overhead of inheritance and virtual functions,
I have to ask, "compared to what"? To get the same functionality
in C, you have to write code that is bigger, slower, and harder to
maintain than in C++. On that basis, assuming your problem lends
itself to an OO design, C++ is a big win.

That said, there are common newcomer errors that lead to grossly
inefficient code in C++, because the syntax is so seductive. The
classic error is passing around large or complex objects by value
instead of by reference. Example:

class Matrix { ... }; // a matrix class
Matrix operator+(Matrix l, Matrix r) { /* return l+r */ }

Here we've defined a Matrix class and a matrix addition function
that takes two matrices by value and returns the sum by value. We
wind up with unneeded extra copies of the input and output matrices
compared to more appropriate designs. (The best way to solve this
problem depends on how you anticipate using matrix addition in
your program.)

Most good C++ texts discuss these sorts of design errors and show you
better ways to write C++.

Two excellent books are "Effective C++" and "More Effective C++"
by Scott Meyers, Addison-Wesley. The books do not teach you C++,
but once you know the C++ basics, show you good techniques for
programming in C++.

--
Steve Clamage, stephen...@sun.com

Francis Glassborow

unread,
Apr 24, 1999, 3:00:00 AM4/24/99
to
In article <37202A7E...@natlab.research.philips.com>, samvit
<sam...@natlab.research.philips.com> writes

>In particular, are there any performance issues to take care of, if we
>decide to move from C to C++.

Yes, the quality of the C++ compiler is very important. Bjarne
Stroustrup reports a 4:1 ration between compilers running some simple
test code (see the current issue of The C/C++ Users Journal)


In many case the best C++ compiler compiled code that ran considerably
faster than the equivalent C code, while the reverse was the case for
poor compilers.

>
>Also can anyone suggest some good references for this study.

Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Sinisa Segvic

unread,
Apr 24, 1999, 3:00:00 AM4/24/99
to
In article <37202A7E...@natlab.research.philips.com>, samvit wrote:
>I am trying to find out whether C++ programs incur any big performance
>penalties in the form of excessive constructor/destructor calls,
>operator overloading, function overloading, inheritance and virtual
>functions.
>
>In particular, are there any performance issues to take care of, if we
>decide to move from C to C++.
>
>Also can anyone suggest some good references for this study.

You can try 'Inside C++ Object Model' by Stan Lippman.

My point of view is the following:
C++ is a superset of C.
Therefore, you are free to decide whether to express a certain
peace of code either using plain C or C++ extensions.
In the real world, things such as free lunch are quite rare
(C++ is no exception) and decision-making almost allways involves
considering trade-offs .

For example, using C++ extensions and programming style rather
than plain C, you are likely to come into position of trading off
the speed for the maintainability and the reusability of the piece of
code in question.

Problems can arise "only" if C++ extensions are used without
considering and understanding the taken trade-offs.

Siemel Naran

unread,
Apr 24, 1999, 3:00:00 AM4/24/99
to
On 24 Apr 1999 00:48:34 -0400, samvit

>I am trying to find out whether C++ programs incur any big performance
>penalties in the form of excessive constructor/destructor calls,
>operator overloading, function overloading, inheritance and virtual
>functions.

There's compile time overhead -- longer compile times, high memory
usage by compiler.

There should be no runtime overhead, although most compilers aren't
there yet.

The compiler should eliminate redundant calls to the copy constructor
and destructor.
X f() { X out; ... ; return out; }
With the return value optimization, there are 0 calls to X's copy
constructor and X's destructor. But most compilers create a local
'out' then copy this local into the return space then destroy the
local.

Declare critical functions inline. A good compiler will use the
as-if optimization to eliminate unnecessary objects, not just
unnecessary object calls!
void f(std::complex<double> a, std::complex<double> b) {
cout << (a+b).real();
}
If operator+(complex,complex), complex::complex(const complex&),
complex::~complex(), complex::real() inline, then the above should
reduce to
void f(std::complex<double> a, std::complex<double> b) {
cout << a.real()+b.real();
}

A compiler can also evaluate const objects at compile time and
possibly eliminate storage for them. Most compilers do the
optimization for builtin types, but not user types.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

Francis Glassborow

unread,
Apr 25, 1999, 3:00:00 AM4/25/99
to
In article <7fsqm9$60p$1...@bagan.srce.hr>, Sinisa Segvic
<sse...@sam.zemris.fer.hr> writes

>For example, using C++ extensions and programming style rather
>than plain C, you are likely to come into position of trading off
>the speed for the maintainability and the reusability of the piece of
>code in question.

Are we talking about compilation speed or speed of the executable. The
evidence that I have seen suggests that it takes considerably longer to
compile C++, but that well written C++ performs as well if not better
than well written C as long as you use a high quality compiler for both.
Most C compilers qualify but many C++ compilers generate poor
executables. As other compilers do much better (even when getting close
to a full feature set for C++) the problem clearly lies, in part, with
the implementation. OTOH poor coding with heavy usage of pass by value
(common among those moving from C to C++) will seriously degrade the
performance of your executable.


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bjarne Stroustrup

unread,
Apr 25, 1999, 3:00:00 AM4/25/99
to
Francis Glassborow <fra...@robinton.demon.co.uk> wites:

> <sam...@natlab.research.philips.com> writes


> >In particular, are there any performance issues to take care of, if we
> >decide to move from C to C++.
>

> Yes, the quality of the C++ compiler is very important. Bjarne
> Stroustrup reports a 4:1 ration between compilers running some simple
> test code (see the current issue of The C/C++ Users Journal)
>
> In many case the best C++ compiler compiled code that ran considerably
> faster than the equivalent C code, while the reverse was the case for
> poor compilers.

In my opinion, those results were more affected by quality differences in
the standard library implementations than by differences in compilers.

- Bjarne
Bjarne Stroustrup - http://www.research.att.com/~bs

Bjarne Stroustrup

unread,
Apr 25, 1999, 3:00:00 AM4/25/99
to
Article 40009 of comp.lang.c++.moderated:

samvit <sam...@natlab.research.philips.com> writes:

> I am trying to find out whether C++ programs incur any big performance
> penalties in the form of excessive constructor/destructor calls,
> operator overloading, function overloading, inheritance and virtual
> functions.
>

> In particular, are there any performance issues to take care of, if we
> decide to move from C to C++.
>

> Also can anyone suggest some good references for this study.

There are no overheads in the C++ language mechanisms mentioned compared
to equivalent C code.

The thing to look out for is inappropriate programming styles (rather
than inefficient language features).

Don't start a major project with everyone at the "for dummies" stage of
expertice. If at all possible, have at least one person on the team who
has been part of a *successful* C++ project (of a kind that is not too
different from what you are attempting.

When learning C++, be suspecious of what sounds like inflated claims
(both for benefits and for problems). Focus on concepts and programming
style (rather than individual language features).

Stay as close to the ISO/ANSI standard as possible.

When looking for books, I recommend a visit to the ACCU website.

Bjarne Stroustrup

unread,
Apr 25, 1999, 3:00:00 AM4/25/99
to

Francis Glassborow <fra...@robinton.demon.co.uk> writes:

> In article <7fsqm9$60p$1...@bagan.srce.hr>, Sinisa Segvic
> <sse...@sam.zemris.fer.hr> writes
> >For example, using C++ extensions and programming style rather
> >than plain C, you are likely to come into position of trading off
> >the speed for the maintainability and the reusability of the piece of
> >code in question.

> Are we talking about compilation speed or speed of the executable. The
> evidence that I have seen suggests that it takes considerably longer to
> compile C++,

Measurements are in order. Most of the "considerably longer to compile C++"
effects that I have seen have been caused by excessive reliance of huge
header files (such as those defining various Windows libraries). This kind
of overhead can be cut drastically (e.g. by an order of magintude or two)
where you are in position to define your own headers and use abstract classes
to cut unecessary direct dependencies on large class hierarchies.


> but that well written C++ performs as well if not better
> than well written C as long as you use a high quality compiler for both.
> Most C compilers qualify but many C++ compilers generate poor
> executables. As other compilers do much better (even when getting close
> to a full feature set for C++) the problem clearly lies, in part, with
> the implementation. OTOH poor coding with heavy usage of pass by value
> (common among those moving from C to C++) will seriously degrade the
> performance of your executable.

Measurements are in order. Call-by-value is often faster than
call-by-reference for small objects. As ever, exact figures are
implementation dependent.

In general, I encourage people who worry about overhead to be precise about
their worries. It is hard to discuss FUD (Fear, Uncertainty, and Doubt)
calmly and constructively. An few simple example that can be measured
focusses a discussion wonderfully.

Consider this trivial example:

struct X {
int a, b;
};

#include<iostream>
#include<ctime>

using namespace std;

int f(const X& r)
{
int res = 0;
for (int i = 0; i<10; i++) { res += r.a + r.b; }
}

int g(const X r)
{
int res;
for (int i = 0; i<10; i++) { res += r.a + r.b; }
}

int main()
{
X x;

cerr << " start\n";
clock_t t = clock();
for (int i = 0; i<10000000; i++) f(x);
cerr << clock()-t << " middle\n";
t = clock();
for (int i = 0; i<10000000; i++) g(x);
cerr << clock()-t << " end\n";
}

It showed me that on one machine using one compiler, the difference
between f() and g() was less about 1% unoptimized and 20% when optimized.

Robert Klemme

unread,
Apr 27, 1999, 3:00:00 AM4/27/99
to
Bjarne Stroustrup schrieb:

>
> Measurements are in order. Call-by-value is often faster than
> call-by-reference for small objects. As ever, exact figures are
> implementation dependent.

Oops. Why is that? I cannot imagine a reason for this...

robert

--
Robert Klemme
Internet Consultant/Berater
-------------------------------------------------------------
UNITY AG ~ Riemekestraße 160 ~ D-33106 Paderborn
http://www.unity.de ~ E-Mail: mailto:kle...@unity.de
Telefon: 0 52 51 / 6 90 90-207 ~ Fax: 0 52 51 / 6 90 90-199
-------------------------------------------------------------

Phlip

unread,
Apr 27, 1999, 3:00:00 AM4/27/99
to
Bjarne Stroustrup wrote:

>Don't start a major project with everyone at the "for dummies" stage of
>expertice. If at all possible, have at least one person on the team who
>has been part of a *successful* C++ project (of a kind that is not too
>different from what you are attempting.

Then lean on a profiler, and don't just make metrics up or guess.

--
Phlip at politizen dot com (address munged)
======= http://users.deltanet.com/~tegan/home.html =======

sa...@bear.com

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <FArs0...@research.att.com>,
b...@research.att.com (Bjarne Stroustrup) wrote:
>
> Francis Glassborow <fra...@robinton.demon.co.uk> writes:

> > OTOH poor coding with heavy usage of pass by value
> > (common among those moving from C to C++) will seriously degrade the
> > performance of your executable.
>

> Measurements are in order. Call-by-value is often faster than
> call-by-reference for small objects. As ever, exact figures are
> implementation dependent.
>

> Consider this trivial example:


>
> struct X {
> int a, b;
> };
>
> #include<iostream>
> #include<ctime>
>
> using namespace std;
>
> int f(const X& r)
> {
> int res = 0;
> for (int i = 0; i<10; i++) { res += r.a + r.b; }
> }
>
> int g(const X r)
> {
> int res;
> for (int i = 0; i<10; i++) { res += r.a + r.b; }
> }
>
> int main()
> {
> X x;
>
> cerr << " start\n";
> clock_t t = clock();
> for (int i = 0; i<10000000; i++) f(x);
> cerr << clock()-t << " middle\n";
> t = clock();
> for (int i = 0; i<10000000; i++) g(x);
> cerr << clock()-t << " end\n";
> }
>
> It showed me that on one machine using one compiler, the difference
> between f() and g() was less about 1% unoptimized and 20% when optimized.

I also thought 'call-by-value' is faster for small objects. When I timed
this program on Solaris 2.5.1, Sun CC 4.2 with -O, I was surprised.

start
2520000 middle
2900000 end

-- Saroj Mahapatra


-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

Stan Brown

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
Dixitque b...@research.att.com (Bjarne Stroustrup) in
comp.lang.c++.moderated:

>Don't start a major project with everyone at the "for dummies" stage of
>expertice. If at all possible, have at least one person on the team who
>has been part of a *successful* C++ project (of a kind that is not too
>different from what you are attempting.

Such a person might well be a hired outside mentor, as recommended in the
FAQ.

I was on a project at a client that started in Java and switched to C++.
Everyone (including me) was at the "for dummies" stage in C++, though not
all believed that. I pushed very hard to get a mentor in, and management
agreed, but changed (what it's pleased to call) its mind the day before
the guy was supposed to arrive.

The impact on the project can be left to the imagination.

--
Stan Brown, Oak Road Systems, Cleveland, Ohio, USA
http://www.mindspring.com/~brahms/
My reply address is correct as is. The courtesy of providing a correct
reply address is more important to me than time spent deleting spam.

Siemel Naran

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
On 27 Apr 1999 08:57:58 -0400, Robert Klemme <kle...@unity.de> wrote:
>Bjarne Stroustrup schrieb:


>> Measurements are in order. Call-by-value is often faster than
>> call-by-reference for small objects. As ever, exact figures are
>> implementation dependent.
>
>Oops. Why is that? I cannot imagine a reason for this...

Here are three reasons.

1. Pass by reference internally passes a pointer.
So if sizeof(object)<sizeof(pointer to object), then you probably
spend more time and space than if you pass the object directly
(more bytes to pass).

2. This also means that each time you use the object, you have to
dereference the pointer.

3. References and pointers are aliases for other objects. Two
pointers may refer to the same object.
int b=1;
void f(const int& a) { cout<<a; b++; cout<<a; }
int main() { f(b); } // prints "1 2"
This examples illustrates why the compiler can't do advanced
optimizations on references. But if we had pass by value
void f(const int a) { cout<<a; b++; cout<<a; }
then the compiler could store 'a' in a register and use it in
both cout statements.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

mfu...@my-dejanews.com

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <3724B00B...@unity.de>,

Robert Klemme <kle...@unity.de> wrote:
> Bjarne Stroustrup schrieb:
> >
> > Measurements are in order. Call-by-value is often faster than
> > call-by-reference for small objects. As ever, exact figures are
> > implementation dependent.
>
> Oops. Why is that? I cannot imagine a reason for this...

Because every access to call-by-reference parameter will cost one extra
dereferencing. And it is hard (if possible) to optimize because of
possible aliasing.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jonathan Thornburg

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <FArs0...@research.att.com>, Bjarne Stroustrup wrote:
> Call-by-value is often faster than
> call-by-reference for small objects. As ever, exact figures are
> implementation dependent.
>

In article <3724B00B...@unity.de>, Robert Klemme <kle...@unity.de>
asked:


>Oops. Why is that? I cannot imagine a reason for this...

Call by reference usually means the arguments have to be in memory
(so we can have pointers or references to them), while call by value
can (and often is) implemented to pass parameters entirely in hardware
registers.

Even if they're in the cache(s), memory references are still almost
always slower than register references. And if any of the memory
references miss the cache(s), the cost is likely to be anywhere from
20 to 100 cycles on modern processors.

--
-- Jonathan Thornburg <jth...@galileo.thp.univie.ac.at>
Universität Wien / Institut für Theoretische Physik
"It's every man for himself, said the elephant as he danced on the
anthill!" -- T. C. "Tommy" Douglas's description
of the central ethos of American society

Sinisa Segvic

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <uSL+4BAJ...@robinton.demon.co.uk>, Francis Glassborow wrote:
>In article <7fsqm9$60p$1...@bagan.srce.hr>, Sinisa Segvic
><sse...@sam.zemris.fer.hr> writes
>>For example, using C++ extensions and programming style rather
>>than plain C, you are likely to come into position of trading off
>>the speed for the maintainability and the reusability of the piece of
>>code in question.
>
>Are we talking about compilation speed or speed of the executable. The
>evidence that I have seen suggests that it takes considerably longer to
>compile C++, but that well written C++ performs as well if not better

>than well written C as long as you use a high quality compiler for both.

I forgot to mention the development effort beside the speed.

While programming in C++, one is usually tempted to achieve
generic code (e.g. having one edge-detecting algorithm that
works for grey level images, paletised images and rgb colour
images) using virtual inheritance.
The resulting code is clear, easily maintainable and easily
upgradeable (to, say, grey level images having more bytes
per pixel) but it is slower than the code that would be
induced by a C programming style:
in C one would write four algorithms, each operating
on a single type of an image.

Now, It is true that virtual inheritance is not
the right mechanism for expressing generic code.
For that purpose, we have templates.
But, my experience with templates is the following:
- the resulting code is harder to maintain
(compiles loooonger and is tedious to debug);
- the resulting code is somewhat less clear;
- it takes two times longer to produce the code;
- yes, the code is considerably faster (about 40%
in the case of the edge detecting algorithm).

Of course, it certanly is the fact that I have begun
writing my own templates only year ago and that
more experient C++ programmer would have done a better
job, but, I simply wasn't lucky enough to meet one :)

My experience with C++ is that programming in it
induces cleaner code which has to be paid either
by speed of the produced object code or by development
effort which surely pays off in large projects.

In the end, I would say that, as far as I know,
poor performance is rarely a cause of unsuccessful
software projects :)

gb...@my-dejanews.com

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <3724B00B...@unity.de>,

Robert Klemme <kle...@unity.de> wrote:
> Oops. Why is that? I cannot imagine a reason for this...
>
> robert
>
I think that's what you should expect. Below is a simple example of how one
compiler, that I will leave unnamed, rendered a C code:
int f(int a)
{
return a+1;
};

int g(int& a)
{
return a+1;
};

int i=0;
f(i);
g(i);
------------ generated code ----------------

push dword ptr [ebp-offset]
call f(int)
;--- int f(int)
push ebp
mov ebp, esp
; return a+1;
mov eax,[ebp+0x08]
inc eax
pop ebp
ret

------
lea eax,[ebp-offset]
push eax ; these two operations are longer
call g(int&)
;--- int g(int&)
push ebp
mov ebp, esp
; return a+1;
mov eax,[ebp+0x08]
mov eax,[eax] ; extra operation
inc eax
pop ebp
ret
-------------------------------------------
there are two extra operations in case of int& that may contribute of 1-5% of
total time.

Gene.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

gb...@my-dejanews.com

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <slrn7i4bo5....@bardeen.ceg.uiuc.edu>,

sbn...@KILL.uiuc.edu wrote:
> The compiler should eliminate redundant calls to the copy constructor
> and destructor.
> X f() { X out; ... ; return out; }

I believe gcc has some construct to implement this behavior. But you cannot
use it in every case. What if the return value of f is not used? - You have
to generate temporary. What if exception is thrown somewhere in the middle? -
If we allow compiler to work directly on return value then we may end up with
some corrupt object.

>
> Declare critical functions inline. A good compiler will use the
> as-if optimization to eliminate unnecessary objects, not just
> unnecessary object calls!
> void f(std::complex<double> a, std::complex<double> b) {
> cout << (a+b).real();
> }
> If operator+(complex,complex), complex::complex(const complex&),
> complex::~complex(), complex::real() inline, then the above should
> reduce to
> void f(std::complex<double> a, std::complex<double> b) {
> cout << a.real()+b.real();
> }

I think it's better to leave for programmer to decide, otherwise we can have
side effects that we don't expect. It's also possible to define some special
class for operators instead of returning complex (in your example).
class complex_operators
{
complex& a;
complex& b;
public:
complex_operators(const complex& a, const complex& b):a(a),b(b){};
double real() { return a.real()+b.real();};
friend ostream& operator <<(ostream&, const complex_operators&);
};

complex_operators operator+(const complex& a, const complex& b)
{ return complex_operators(a,b);}
It won't save time for trivial objects like complex, but it can be a big time
saver for those who write matrix or vector or other big objects.

>
> A compiler can also evaluate const objects at compile time and
> possibly eliminate storage for them. Most compilers do the
> optimization for builtin types, but not user types.

How is it going to happen? Example, const TBigMatrix
BigMatrix(some_file_to_read_data_from); ... // somewhere in different places
TBigMatrix A(BigMatrix); TBigMatrix B(BigMatrix); ... how can compiler
eliminate BigMatrix object if it doesn't know when A and B are constructed?
Or maybe it will substitute that calls with TBigMatrix
A(TBigMatrix(some_file_to_read_data_from)); TBigMatrix
B(TBigMatrix(some_file_to_read_data_from)); that would be terrible waste of
time and space and cause erroneous behavior in some cases.

Robert O'Dowd

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
Robert Klemme wrote:
>
> Bjarne Stroustrup schrieb:
> >
> > Measurements are in order. Call-by-value is often faster than

> > call-by-reference for small objects. As ever, exact figures are
> > implementation dependent.
>
> Oops. Why is that? I cannot imagine a reason for this...
>
> robert
>

Lots of reasons, all implementation sensitive to make your milage
vary. I'll try to be generic....but may not really succeed.
When you start talking performance trade-offs you are well
outside the domain of the C++ standard.

Argument passing ultimately gets down to passing *something* by
value. Passing by value implies taking a copy of the value
you are sending to the function. Let's look at a function
that accepts a char by value.

void func(char x);

This works by passing a one byte value by value. When a caller
does

func('A');

your function receives a copy of the byte 'A'. That is the
reason that (say) func() can modify its argument x, without
that change being seen by the caller. In this case, we have
one byte being copied.

With some oversimplification, pass by reference works by
passing a pointer. This means that callers of

void func2(const char &x);

implicitly pass an address by value. On most systems,
addresses require storage of > one byte. On at least
some of those, copying one byte will by quicker than
copying an address. So, ON THOSE SYSTEMS, passing
a char by value will be quicker than passing it by reference.

In practice, compiler writers will run into many issues like
byte alignment on a given hardware platform and operating
system. These systems can break the idea that copying
one byte is quicker than copying a larger number. This
is the reason that some compilers give warnings about
byte alignment and padding. For that reason (and many
others), if you are worried about performance, the only
reliable method is to try out all options.

-<Automagically included trailer>
Robert O'Dowd Ph +61 (8) 8259 6546
MOD/DSTO Fax +61 (8) 8259 5139
P.O. Box 1500 Email:
robert...@dsto.defence.gov.au
Salisbury, South Australia, 5108

Disclaimer: Opinions above are mine and may be worth what you paid for
them

s...@cae.co.uk

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <FArCw...@research.att.com>,

b...@research.att.com (Bjarne Stroustrup) wrote:
> Francis Glassborow <fra...@robinton.demon.co.uk> wites:
>
> > In article <37202A7E...@natlab.research.philips.com>, samvit
> > <sam...@natlab.research.philips.com> writes
> > >In particular, are there any performance issues to take care of, if we
> > >decide to move from C to C++.
> >
> > Yes, the quality of the C++ compiler is very important. Bjarne
> > Stroustrup reports a 4:1 ration between compilers running some simple
> > test code (see the current issue of The C/C++ Users Journal)
> >
> > In many case the best C++ compiler compiled code that ran considerably
> > faster than the equivalent C code, while the reverse was the case for
> > poor compilers.
>
> In my opinion, those results were more affected by quality differences in
> the standard library implementations than by differences in compilers.
>
> - Bjarne

A 4:1 ratio is certainly significant, and possibly essential in helping to
choose a particular C++ compiler.

Do we have any published comparisons for the performances of various
compilers? Are there any standard sources for bench-marking? And finally,
does such a publication provide insight as to where the bottlenecks are (e.g.
standard library implementations)?

Has Bjarne published his own performance suites/results?

Steve Morley
CAE Electronics PLC

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Siemel Naran

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
On 28 Apr 1999 09:02:35 -0400, Stan Brown <bra...@mindspring.com> wrote:

>[snip]


>I pushed very hard to get a mentor in, and management
>agreed, but changed (what it's pleased to call) its mind the day before
>the guy was supposed to arrive.
>
>The impact on the project can be left to the imagination.

My imagination is not that good today. What happened?

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

kwil...@my-dejanews.com

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to
In article <7g49o1$irp$1...@nnrp1.dejanews.com>,

sa...@bear.com wrote:
>
> I also thought 'call-by-value' is faster for small objects. When I timed
> this program on Solaris 2.5.1, Sun CC 4.2 with -O, I was surprised.
>
> start
> 2520000 middle
> 2900000 end
>

Here are some more results:

Solaris 2.5.1, Sun CC 5.0 with -O, Ross CPU

start
4630000 middle
4290000 end

Solaris 2.5.1, Sun CC 5.0 with -O, Ultra

start
1820000 middle
2140000 end

This is with exactly the same executable! The only difference is the CPU
architecture. So we can't say which is faster, in general.

The -O5 result is quite interesting:

start
0 middle
0 end

So much for short test benchmarks! We conclude that pass by value and pass
by reference are both equally quick - as long as the function called doesn't
do anything and is therefore removed by the optimiser!

:)

Karl.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bjarne Stroustrup

unread,
Apr 28, 1999, 3:00:00 AM4/28/99
to

Robert Klemme <kle...@unity.de> skriver:

> Bjarne Stroustrup schrieb:
> >
> > Measurements are in order. Call-by-value is often faster than
> > call-by-reference for small objects. As ever, exact figures are
> > implementation dependent.
>
> Oops. Why is that? I cannot imagine a reason for this...

To pass an int by reference, you need to copy a pointer to the int;
that's as expensive as copying the int. Accessing the int through
the pointer could easily be more expensive than accessing the copy
of the int on the stack.

Passing two ints vs passing a reference to a structure containing two ints
vs copying a structure containing two ints gives many opportunities for
tradoffs depending on machine architectures and optimizer strategies.

As the amount of data increases the tradeoffs increasingly favor
pass-by-reference (e.g. passing a 1Mb picture by value is almost
certainly a bad idea).

- Bjarne
Bjarne Stroustrup - http://www.research.att.com/~bs

Francis Glassborow

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
In article <7g49o1$irp$1...@nnrp1.dejanews.com>, sa...@bear.com writes

>I also thought 'call-by-value' is faster for small objects. When I timed
>this program on Solaris 2.5.1, Sun CC 4.2 with -O, I was surprised.
>
> start
>2520000 middle
>2900000 end

IOWs if it might matter, measure. BTW this is another reason for using
reference parameters rather than pointers, it is very easy to try pass
by reference and pass by value.


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Siemel Naran

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
On 28 Apr 1999 13:38:58 -0400, gb...@my-dejanews.com

>> A compiler can also evaluate const objects at compile time and
>> possibly eliminate storage for them. Most compilers do the
>> optimization for builtin types, but not user types.
>
>How is it going to happen? Example, const TBigMatrix
>BigMatrix(some_file_to_read_data_from); ... // somewhere in different places

This constructor relies on an external location -- namely the file to
read from. This value of BigMatrix cannot possibly known at compile
time. So the optimization doesn't apply. I had in mind simple things
like std::complex<double> and double[].

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jerry Leichter

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
| >> Measurements are in order. Call-by-value is often faster than
| >> call-by-reference for small objects. As ever, exact figures are
| >> implementation dependent.
| >
| >Oops. Why is that? I cannot imagine a reason for this...
|
| Here are three reasons.
|
| 1. Pass by reference internally passes a pointer.
| So if sizeof(object)<sizeof(pointer to object), then you probably
| spend more time and space than if you pass the object directly
| (more bytes to pass).

I know of no modern architecture on which this is true (and any examples
I could think of are so old they probably don't have C compilers, much
less C++ compilers, on them).

Almost all machines pass at least the first couple of arguments
(typically, the first 6 or so) in registers. Registers are, on all
modern machines, large enough to hold an address. Loading a register
takes the same amount of time whether you load a byte or the whole
register. (In fact, on some machines loading just a byte may be quite a
bit *more* expensive, because you have to deal with unaligned access.)

Some older but still "live" architectures pass arguments on an in-memory
stack. While a stack could, in theory, have enough space for just one
byte for a byte argument, in practice no one does this: It would leave
the stack unaligned, and even if the machine supports unaligned
addresses, using them costs you.

Beyond that, the path to memory is typically at least as wide as an
address, so reading or writing a byte is no cheaper than reading or
writing the whole word it's in.

There might be exceptions for some very low-end machines used in
embedded systems, but they would be quite rare.



| 2. This also means that each time you use the object, you have to
| dereference the pointer.

Yes, and this is an unfortunate side-effect of the way C++ builds "call
by reference" out of references. Many languages with "call by
reference" deliberately allow for either true call by reference, or
copy-in copy-out semantics. The latter - not necessarily implemented as
a real copy - can be essentially equivalent in efficiency to call by
value. However, in the presence of aliasing, it doesn't have the same
semantics - and C++ allows aliasing.

| 3. References and pointers are aliases for other objects. Two
| pointers may refer to the same object.
| int b=1;
| void f(const int& a) { cout<<a; b++; cout<<a; }
| int main() { f(b); } // prints "1 2"
| This examples illustrates why the compiler can't do advanced
| optimizations on references. But if we had pass by value
| void f(const int a) { cout<<a; b++; cout<<a; }
| then the compiler could store 'a' in a register and use it in
| both cout statements.

Yup. This is exacty the kind of optimization that FORTRAN - which uses
call by reference everywhere - is still allowed to perform, and in some
code, it can have significant effects on the optimizations allowed.
That's why C9x has the "restrict" type qualifier.

-- Jerry

sa...@bear.com

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
In article <5S97YwAV...@robinton.demon.co.uk>,

Francis Glassborow <fran...@robinton.demon.co.uk> wrote:
> In article <7g49o1$irp$1...@nnrp1.dejanews.com>, sa...@bear.com writes
> >I also thought 'call-by-value' is faster for small objects. When I timed
> >this program on Solaris 2.5.1, Sun CC 4.2 with -O, I was surprised.
> >
> > start
> >2520000 middle
> >2900000 end
>
> IOWs if it might matter, measure. BTW this is another reason for using
> reference parameters rather than pointers, it is very easy to try pass
> by reference and pass by value.
>

I do not use pointer where pass by value or by const reference is
preferable. On the other hand, I prefer pointers to non-const reference
unless it is the return value of an operator. It makes the intention
clear at the time of call.

-- Saroj Mahapatra

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bengt Richter

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
On 28 Apr 1999 21:31:48 -0400, b...@research.att.com (Bjarne Stroustrup)
wrote:

>
>Robert Klemme <kle...@unity.de> skriver:
>
> > Bjarne Stroustrup schrieb:
> > >

> > > Measurements are in order. Call-by-value is often faster than
> > > call-by-reference for small objects. As ever, exact figures are
> > > implementation dependent.
> >
> > Oops. Why is that? I cannot imagine a reason for this...
>

>To pass an int by reference, you need to copy a pointer to the int;

OTOH this could be optimized away in an in-lined function?

>that's as expensive as copying the int. Accessing the int through
>the pointer could easily be more expensive than accessing the copy
>of the int on the stack.
>
>Passing two ints vs passing a reference to a structure containing two ints
>vs copying a structure containing two ints gives many opportunities for
>tradoffs depending on machine architectures and optimizer strategies.
>

Bottom line: You can't be sure what you're going to get
besides correct result? So peek at machine language for indications,
and measure to be sure?

>As the amount of data increases the tradeoffs increasingly favor
>pass-by-reference (e.g. passing a 1Mb picture by value is almost
>certainly a bad idea).

You're *almost* expecting a counter-argument? :)


>
> - Bjarne
>Bjarne Stroustrup - http://www.research.att.com/~bs

Does all this mean that to be a good C++ programmer
one used to have to be a good arms-length assembler programmer
writing C++ with an intended (even if approximate) machine language
effect in mind, whereas now we-only-need-to/can-only know enough
to avoid things that are "... almost certainly a bad idea"?

Is "Measurements are in order" the bottom line? If so,
what do I have to know about the hardware/compiler to make a
code change with the expectation of a performace change worth
measuring? Are there things that are still *not* best left to
the compiler, when we have speculative and out-of-order multiple
execution pipelines, queued memory accesses, interleaved memory,
different cache sizes, line sizes, organization, and on and on,
not to mention multiple cooperating processors?

Obviously (?) a compiler can't optimize by reformulating
my algorithms (e.g., shifting a coordinate system to generate
eliminatable zero terms in oft-evaluated expressions, etc.), but
assuming best use of hardware/compiler will remain important, what
kinds of hardware/compiler knowledge will remain important for
programmers in the forseeable future?

Regards,
Bengt Richter
[post to c.l.c++.m attempted]

nik...@my-dejanews.com

unread,
Apr 29, 1999, 3:00:00 AM4/29/99
to
In article <37202A7E...@natlab.research.philips.com>,

samvit <sam...@natlab.research.philips.com> wrote:
> I am trying to find out whether C++ programs incur any big performance
> penalties in the form of excessive constructor/destructor calls,
> operator overloading, function overloading, inheritance and virtual
> functions.
> Also can anyone suggest some good references for this study.

In the past I've heard people mutter about the overheads
of these things. I find that a simple way to demonstrate
that there are no significant overheads is to compile a small
demonstration program and look at the assembly output.

These things sound very impressive on paper, but down
at the assembly level they're fairly simple. Of course,
this means that you need some understanding of assembly
language. A bit of "hackers way" of looking at thing,
but I found it helps to know what is happening down at
this level, especially where time considerations are
important.


--
Martin Nike
Software Engineer, Marconi Communication,
Coventry, UK
"Nothing, like something, happens anywhere."

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

mar...@my-dejanews.com

unread,
Apr 30, 1999, 3:00:00 AM4/30/99
to
In article <37276D...@smarts.com>,

Jerry Leichter <leic...@smarts.com> wrote:
> | >> Measurements are in order. Call-by-value is often faster than
> | >> call-by-reference for small objects. As ever, exact figures are
> | >> implementation dependent.
> | >
> | >Oops. Why is that? I cannot imagine a reason for this...
> |
> | Here are three reasons.
> |
> | 1. Pass by reference internally passes a pointer.
> | So if sizeof(object)<sizeof(pointer to object), then you probably
> | spend more time and space than if you pass the object directly
> | (more bytes to pass).
>
> I know of no modern architecture on which this is true (and any examples
> I could think of are so old they probably don't have C compilers, much
> less C++ compilers, on them).
>
> Almost all machines pass at least the first couple of arguments
> (typically, the first 6 or so) in registers. Registers are, on all
> modern machines, large enough to hold an address.

You have missed the point. A "small" object will probably already be in a
register. Passing by value is (at most) a register to register move, and
possibly not even that (it may already be in the correct register). Passing
by reference involves storing the register to memory, getting the address of
the memory location into a register, and then inside the function loading the
value from memory again.

So although you are correct to the extent that in both cases the parameter is
simply passed in a register, there can be significantly more overhead for the
reference case.

Of course, there is also the pathalogical case where the small object is
already in memory, and the called function needs it to be in memory, so pass
by value involves a load in the caller, followed by a store in the callee...
but that is typically far less common (particularly with well optimized
code).

> Loading a register
> takes the same amount of time whether you load a byte or the whole
> register. (In fact, on some machines loading just a byte may be quite a
> bit *more* expensive, because you have to deal with unaligned access.)

But this is irrelevant, since in the pass by reference case, the callee will
still have to obtain the value referred to...

Mark Williams

James...@dresdner-bank.com

unread,
Apr 30, 1999, 3:00:00 AM4/30/99
to
In article <7g6pfv$qp2$1...@nnrp1.dejanews.com>,
s...@cae.co.uk wrote:

> Do we have any published comparisons for the performances of various
> compilers? Are there any standard sources for bench-marking? And finally,
> does such a publication provide insight as to where the bottlenecks are (e.g.
> standard library implementations)?

I'm not sure what standard benchmarks are really worth. For certain
specific domains, like number crunching, there are domain specific
benchmarks, like LinPack, which might be worthwhile, but in general, you
almost always need an application specific benchmark. Not really
recent, but I have two separate benchmarks comparing Sun CC 4.2 and g++
2.8.2. One shows the Sun compiler a clear winner, the other favors
g++. Which one is "correct"? It depends on what you do in your
application.

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49 (069) 63 19 86 27

Robin Rosenberg

unread,
May 1, 1999, 3:00:00 AM5/1/99
to
sa...@bear.com wrote:

> I also thought 'call-by-value' is faster for small objects. When I timed
> this program on Solaris 2.5.1, Sun CC 4.2 with -O, I was surprised.
>
> start
> 2520000 middle
> 2900000 end

Now, the question is: What are you measuring? The time to add something
ten times or something else?. Now here is the suprise (MSVC 6.0).

10: int f(const X& r)
11: {
00401080 mov ecx,dword ptr [esp+4] -- get &r
00401084 mov eax,dword ptr [ecx+4] -- get r.b
00401087 mov edx,dword ptr [ecx] -- get r.a
00401089 add eax,edx -- temp = r.a+r.b
0040108B lea eax,[eax+eax*4] temp=temp*5
0040108E shl eax,1 temp=temp*10
12: int res = 0;
13: for (int i = 0; i<10; i++) { res += r.a + r.b; }
14: return res;
15: }
00401090 ret

Summary: The loop is unrolled!

--
Robin Rosenberg | Voice: +46-708-305342 | "Any opinions are my own, etc.
etc."3
Copyright © 1999 | ICQ#: 4058565 | <this line left blank>
Rosenberg IT AB | Mail: rob...@acm.org |

John Potter

unread,
May 1, 1999, 3:00:00 AM5/1/99
to
sa...@bear.com wrote:

: I do not use pointer where pass by value or by const reference is


: preferable. On the other hand, I prefer pointers to non-const reference
: unless it is the return value of an operator. It makes the intention
: clear at the time of call.

To you and to me; however, it is an implementation detail which I prefer
not to see at the call site. I prefer a function whose name indicates
what it is doing to my arguments. I want to have an interface which
lets me use it, not makes me do something special to use it.

YMMV
John

Steve Clamage

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
bo...@accessone.com (Bengt Richter) writes:

>> > > Measurements are in order. Call-by-value is often faster than
>> > > call-by-reference for small objects. As ever, exact figures are
>> > > implementation dependent.

> Bottom line: You can't be sure what you're going to get


>besides correct result? So peek at machine language for indications,
>and measure to be sure?

This kind of micro-optimization should be investigated only
when you have discovered that performance of the function is
a bottleneck and you have already taken care of the big
items that affect performance.

> Does all this mean that to be a good C++ programmer
>one used to have to be a good arms-length assembler programmer
>writing C++ with an intended (even if approximate) machine language
>effect in mind,

Not in general. In fact, the opposite can be true! If you
write code intending to get a particular machine-language
effect, the effort can be counter-productive whenever the
compiler does not generate the code you are expecting. If
you port to a different platform, the tradeoffs can be
entirely different. A different release of a compiler on
the same platform might use a different code-generation
strategy, making your "optimization" worse than useless.

> Is "Measurements are in order" the bottom line? If so,
>what do I have to know about the hardware/compiler to make a
>code change with the expectation of a performace change worth
>measuring? Are there things that are still *not* best left to
>the compiler, when we have speculative and out-of-order multiple
>execution pipelines, queued memory accesses, interleaved memory,
>different cache sizes, line sizes, organization, and on and on,
>not to mention multiple cooperating processors?

Worry first about the big things that affect performance:
use the appropriate data structures and algorithms for the
task at hand. A good choice can make orders-of-magnitude
improvement over poor choices.

Worry next about compiler-independent coding techniques.
Scott Meyers covers some of these, as do other books.
Examples are preferring initialization to assignment,
and watching out for temporaries created by passing
values of class type to and from functions. You can waste
lots of time creating and destroying temporaries,
independent of the compiler and platform.

Worry next -- after you have measured performance, found it
to be inadequate, and discovered the bottleneck -- about
customizing your code to the application. Sometimes you
pay a peformance price for flexibility. Sometimes you are
willing to pay a code maintenance price at some hypothetical
future time to get better performance now. (But you must
make such tradeoffs as a considered decision.)

Only when measurement has shown that you need to look
for smaller effects should you consider testing the
difference between passing a fundamental type by reference
and by value. And be aware that whatever you find with one
compiler/platform combination might not carry over to any
other compiler or platform.

--
Steve Clamage, stephen...@sun.com

cletu...@my-dejanews.com

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
In article <7g58sg$1on$1...@bagan.srce.hr>,

sse...@sam.zemris.fer.hr (Sinisa Segvic) wrote:
> In article <uSL+4BAJ...@robinton.demon.co.uk>, Francis Glassborow wrote:
> >In article <7fsqm9$60p$1...@bagan.srce.hr>, Sinisa Segvic
> ><sse...@sam.zemris.fer.hr> writes
> >>For example, using C++ extensions and programming style rather
> >>than plain C, you are likely to come into position of trading off
> >>the speed for the maintainability and the reusability of the piece of
> >>code in question.
> >
> >Are we talking about compilation speed or speed of the executable. The
> >evidence that I have seen suggests that it takes considerably longer to
> >compile C++, but that well written C++ performs as well if not better
> >than well written C as long as you use a high quality compiler for both.
>
> I forgot to mention the development effort beside the speed.
>
> While programming in C++, one is usually tempted to achieve
> generic code (e.g. having one edge-detecting algorithm that
> works for grey level images, paletised images and rgb colour
> images) using virtual inheritance.

To me, this is the correct approach to this particular problem
domain.

> The resulting code is clear, easily maintainable and easily
> upgradeable (to, say, grey level images having more bytes
> per pixel) but it is slower than the code that would be
> induced by a C programming style:
> in C one would write four algorithms, each operating
> on a single type of an image.
>
> Now, It is true that virtual inheritance is not
> the right mechanism for expressing generic code.
> For that purpose, we have templates.

Here I disagree. IMHO templates are the wrong mechanism to
express this behaviour. Why? Because the behaviour of each
parameterised class varies in behaviour. I refer to the
(excellent) book "Effective C++" by Scott Meyers, Item 41:
Difference Between Inheritance and Templates:

o A template should be used to generate a collection of
classes when the type of the objects does not affect the
behavior of the class's functions.

o Inheritance should be used for a collection of classes
when the type of the objects does affect the behavior of
the class's functions.

Needless to say, an excellent example is provided as to why
this is the case.

And from the particular problem specification used, I'd say
its clear the behavior of the class's functions very much do
differ between the different objects (algorithms).

Which of course brings us to the C++ performance overhead.
Sure using an interface of virtual functions with inheritance
will take a performance hit (through the runtime vtable lookup)
over a statically linked function but there are several things
you have to bear in mind:

1. You could easily take the C approach in C++ for the same
performance level but that (obviously) defeats the purpose of
using C++.

2. Its not so much as a performance hit as it is a tradeoff.
You're not really taking a performance hit for using C++, you're
making a tradeoff of performance vs. abstraction/encapsulation
(eg you can define functions that accept an abstract object and
thus don't care about the particulars of how its implemented).

> But, my experience with templates is the following:
> - the resulting code is harder to maintain
> (compiles loooonger and is tedious to debug);

One thing that has become clear to me from using the STL is that
templates certainly do have their idiosynchracies. If, for
example, you use a class as one of your template arguments (eg
set<A, Less<A> >) but A doesn't satisfy the contract of that
class then you usually end up with a 12 line error message thats
not always obvious as to what its really saying.

> - the resulting code is somewhat less clear;

I disagree. If used correctly (eg using the above guidelines of
templates vs inheritance) you can yield very clear code. Templates
are prone to misuse and obfuscation like any other language feature.

> - it takes two times longer to produce the code;

I'm not sure what you mean here. Using templates has always saved
me development time.

> - yes, the code is considerably faster (about 40%
> in the case of the edge detecting algorithm).
>
> Of course, it certanly is the fact that I have begun
> writing my own templates only year ago and that
> more experient C++ programmer would have done a better
> job, but, I simply wasn't lucky enough to meet one :)
>
> My experience with C++ is that programming in it
> induces cleaner code which has to be paid either
> by speed of the produced object code or by development
> effort which surely pays off in large projects.

My take on it is that C++ development leads to greater
time spent in analysis and design, the same or less time
in development and alot less time in maintenance. Thats
just from my own experience.


>
> In the end, I would say that, as far as I know,
> poor performance is rarely a cause of unsuccessful
> software projects :)

Never a truer word was spoken. :-)

Regards
Billy

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Scott Meyers

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
On 30 Apr 1999 22:05:00 -0400, Jerry Leichter wrote:
> The advice we keep hearing - "measure, don't guess" - is *wrong* for the
> majority of programs, the majority of programmers, most of the time.
> The *right* advice is: Write for clarity/maintainability/correctness.
> If you have to measure - it *probably* doesn't matter.

I think the real advice (which I believe was implicit in Bjarne's post) is
"Don't worry about peformance until you know you have a problem. When you
know you have a performance problem, don't guess at the cause, use
empirical studies to find the cause." In other words, behavior first,
performance second -- if at all. I agree with you that for many programs
and many programmers, there's no need to worry about tuning performance if
you've applied reasonable and straightforward coding practices in the first
place (e.g., avoided unnecessary copying of data, etc.).

However, I'm currently consulting on a project that has very much embraced
the "behavior first, performance second" philosophy, and though the program
behaves quite nicely, it's so slow as to be unusable. (Trust me,
"unusable" in this case is an objective statement :-}) My next big
undertaking is to try to find a way to improve its performance by between
one and two orders of magnitude. A speedup of 100 would get it into the
ballpark we need for a decent initial release. In this case, we *know* we
have a performance problem, and my first step will be to head straight for
one or more profilers. This task is too important for guessing.

Scott

--
Scott Meyers, Ph.D. sme...@aristeia.com
Software Development Consultant http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD

Phlip

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
Scott Meyers wrote:

>However, I'm currently consulting on a project that has very much
embraced
>the "behavior first, performance second" philosophy, and though the
program
>behaves quite nicely, it's so slow as to be unusable. (Trust me,
>"unusable" in this case is an objective statement :-}) My next big
>undertaking is to try to find a way to improve its performance by
between
>one and two orders of magnitude.

Far too often posts on the C++ newsgroups say "this code looks all
clean and easy to read, but I'm thinking of mucking it up and making
it illegible so it's faster." These situations elicit a canned
warning that the neophyte attempts to indulge in "premature
optimization". There are those who sign their e-mails "'Premature
optimization is the root of all evil.' -- Donald E Knuth", so I'l
take their word for it that this "Knuth" guy first said it.

But it's not as simple as such a generic aphorism. Until the code is
fully written and can be tested, programmer time is more important
than CPU time; the current phase of the project influences how to
feed the need for speed. Early in design, at at the 35,000 foot view
of the project, one should determine - quite prematurely - that the
algorithms selected perform no unneeded data movement, and that the
links between components have sufficient bandwidth for their
expected loads, and that nobody creates data that nobody else
actually needs.

In the coding phase, one can still type very fast even while one
applies many simple rules that provide for code the compiler likes.
In C++, one passes references of "large" things by default, where
the programmer may guess at what threshold "large" applies. And at
the level of objects and their interactions, one should be aware of
situations where data get created but then destroyed and then
created again; even if this happens across encapsulation boundaries
where you'r not "supposed" to worry about it in OO terms.

Profiling finally allows CPU time to be more important than
programmer time.

>A speedup of 100 would get it into the
>ballpark we need for a decent initial release. In this case, we
*know* we
>have a performance problem, and my first step will be to head
straight for
>one or more profilers. This task is too important for guessing.

If I played golf I could get away with an expression like "never go
golfing with only one club in your bag." >Ilk< - I can't tell if
_that_ generic aphorism works or not from my golf-free point of
view.

I'd profile with an automated 4th-party profiler, _and_ with a
hand-made profiler like this:

struct
Tracer {
string m_strFile;
int m_nLine;
string m_strFunction;
clock_t m_ticks;
Tracer (string strFile, int nLine, string strFunction):
m_strFile (strFile), m_nLine (nLine), m_strFunction
(strFunction) {
cout << strFile << '(' << nLine << "):" << strFunction << "
Start";
m_ticks = clock ();
}
~Tracer () {
m_ticks = clock () - m_ticks;
cout << m_strFile << '(' << m_nLine << "):" << m_strFunction
<< m_ticks << " ticks";
}
};

#ifdef DEBUG
# define TRACER_ Tracer z_ (__FILE__, __LINE__,
__PRETTY_FUNCTION__)
#else
# define TRACER_
#endif

void
MyFunk ()
{
TRACER_;
// ...
}

The only odious part is you must put TRACER_; at the start of each
function. But once you do that, you can expand the contents of
'Tracer' all over the place. For example, you can add a static list
container; register each Tracer instance when it constructs, and
remove it when it destructs. This would provide a "stack dump" for
the 'assert' statements.

But you'd also add a static Tree-shaped container, push every single
call as an object into that, and finish the program with code that
mines this tree for info.

Yes, this strategy skews the code speed a little, but you'd only
expect to use it for a coarse-grained profile to quickly locate the
bottlenecks, then to quickly track them across tweak-n-test cycles
without a "real" profiler getting in the way or offering info you
don't need; you could also use it in conjunction with your unit test
rigs to automatically check if things "got slow again".

I have used a variation on 'Tracer' that also captures one of the
arguments to the function, like this:

void
MyFunk (string strFileName)
{

#ifdef DEBUG
TRACER_;
z_.strWhat = strFileName;
#endif

//...

}

Then while mining the result I print out the tick count of the
slowest call to 'MyFunk', followed by the name of the item that made
it slow. I'm not sure if "real" profilers can do that...

--
Phlip at politizen dot com (address munged)
======= http://users.deltanet.com/~tegan/home.html =======
-- Work is the source of the root of all evil --

Sinisa Segvic

unread,
May 3, 1999, 3:00:00 AM5/3/99
to
In article <7gg73t$pj4$1...@nnrp1.dejanews.com>, cletu...@my-dejanews.com wrote:
>> Now, It is true that virtual inheritance is not
>> the right mechanism for expressing generic code.
>> For that purpose, we have templates.
>
>Here I disagree. IMHO templates are the wrong mechanism to
>express this behaviour. Why? Because the behaviour of each
>parameterised class varies in behaviour.

Not necessarily - it depends on what you take for template parameter.

If you take a simple data type for template argument (say, an integer
indicating the size of pixel) than you would have a problem writing
generic code for finding the difference between two adjacent pixels:
- in grey images having 8 bits per pixels the operation
boils down to simple subtraction d=(p2-p1);
- in rgb images having 3*8=24 bits per pixel, the operation
is much more complicated (if the simple euclidian colour
metric is used): d=sqrt((r2-r1)^2+(g2-g1)^2+(b2-b1)^2).
In this case your argumentation is correct.

>(excellent) book "Effective C++" by Scott Meyers, Item 41:
>Difference Between Inheritance and Templates:
>
> o A template should be used to generate a collection of
> classes when the type of the objects does not affect the
> behavior of the class's functions.
>
> o Inheritance should be used for a collection of classes
> when the type of the objects does affect the behavior of
> the class's functions.

In the example above, using a complex (possibly abstract) data type
as template argument, you can achieve code which conforms to #1 above.
The idea is to replace virtual call with the inlined call
of the static member function of the template argument.


>Needless to say, an excellent example is provided as to why
>this is the case.
>And from the particular problem specification used, I'd say
>its clear the behavior of the class's functions very much do
>differ between the different objects (algorithms).

Here enters the development effort part - you've got to
design the teplate argument.
See above.

>1. You could easily take the C approach in C++ for the same
>performance level but that (obviously) defeats the purpose of
>using C++.

Yes, but I prefer having one copy of the algorithm than
four because it is much nicer having to debug and improve
once than four times.

>2. Its not so much as a performance hit as it is a tradeoff.
>You're not really taking a performance hit for using C++, you're
>making a tradeoff of performance vs. abstraction/encapsulation
>(eg you can define functions that accept an abstract object and
>thus don't care about the particulars of how its implemented).

That's what I said in the first place :)
However, if you want the best from both worlds,
you've got to pay for it :(

>> - the resulting code is somewhat less clear;
>I disagree. If used correctly (eg using the above guidelines of
>templates vs inheritance) you can yield very clear code. Templates
>are prone to misuse and obfuscation like any other language feature.

Of course, if you use simple template arguments the obtained code
is much more clear than if you use complex ones.



>
>> - it takes two times longer to produce the code;
>I'm not sure what you mean here. Using templates has always saved
>me development time.

I was referring to the above example.
In general, if you make a template taking
a built-in data type as an argument, you are probably right.

Bjarne Stroustrup

unread,
May 4, 1999, 3:00:00 AM5/4/99
to

sme...@aristeia.com (Scott Meyers) writes:

> On 30 Apr 1999 22:05:00 -0400, Jerry Leichter wrote:
> > The advice we keep hearing - "measure, don't guess" - is *wrong* for the
> > majority of programs, the majority of programmers, most of the time.
> > The *right* advice is: Write for clarity/maintainability/correctness.
> > If you have to measure - it *probably* doesn't matter.
>
> I think the real advice (which I believe was implicit in Bjarne's post) is
> "Don't worry about peformance until you know you have a problem. When you
> know you have a performance problem, don't guess at the cause, use
> empirical studies to find the cause." In other words, behavior first,
> performance second -- if at all. I agree with you that for many programs
> and many programmers, there's no need to worry about tuning performance if
> you've applied reasonable and straightforward coding practices in the first
> place (e.g., avoided unnecessary copying of data, etc.).

I think the caveat is extremely important and I rarely - if ever - forget to
make it in my writings. "The C++ Programming Language (2nd and 3rd editions)"
is explicit about considering efficiency *during the design phase* of a
project.

My recent paper on "Learning Standard C++ as a New language" devotes over
a third of its space to demonstrate how a high-level approach can avoid
inefficiencies. I consider it irresponsible to expose novices to an approch
that initially relies on inefficient and/or error-prone techniques (and
only later - if at all - teach them to overcome those problems).

Reasonable efficiency is part of the behavior of a program - and is often
be part of the specification of a system. For example, "response time is
less than one second" is a fairly conventional requirement. A good design
cannot ignore basic efficiency requirements.

This implies that designers and programmers must have a reasonable model
for platform performance (how must does it cost to use the disk? how much
does a remote procedure call cost? what is the basic performance
implications of the creation of objects of various forms?).

In addition, the designers must have some basic understanding of the use
that their program/system makes of the platform. Are the basic algorithms
of a reasonable order? (quadratic algorithms for millions of values are
ridiculous independently of the speed of low-level detailes such as
call-by-value vs call-by-reference for small objects - the isssue that
started this thread). Does basic operations involve many inter-process or
inter-component calls? (I have seen designs where a single simple operation
involved dozens of inter-component calls and the examination of several
"environment variables" which were kept on disk - again no programming
language performance can make such a design perform fast).


> However, I'm currently consulting on a project that has very much embraced
> the "behavior first, performance second" philosophy, and though the program
> behaves quite nicely, it's so slow as to be unusable. (Trust me,
> "unusable" in this case is an objective statement :-}) My next big
> undertaking is to try to find a way to improve its performance by between

> one and two orders of magnitude. A speedup of 100 would get it into the


> ballpark we need for a decent initial release. In this case, we *know* we
> have a performance problem, and my first step will be to head straight for
> one or more profilers. This task is too important for guessing.

The traditional description such a system is "you need a calendar, not a
stopwatch to measure its performance" :-)

I'm not arguing that people should focus on the cost of function calls, the
cost of various forms of argument passing, and the cost of using pointers
rather than subscripting to access a vector. Such factors are largely
reasonable in current C++ implementations (though factor-of-two improvements
are possible in some cases).

Rather, people should have a basic understanding of cost factors so that they
can focus on what matters for a given project. If you are refreshing a
megapixel display, you have one set of concerns, if you are doing transactions
across a LAN you have another set. Only if you have some basic underst
anding
of the factors that affect your costs can you hope to spend your time and
effort wisely. Some basic measurements early on in a project can be most
valuable in avoiding debacles such as the one Scott described.

I suspect that the bottom line is that you can afford a pure "behavior first,
performance second if at all" only if you happen to know the basic performance
characteristics of your platform and your application. Also, performance
measurements never fail to surprise and amaze me - guessing about performance
is indeed hazardous.

- Bjarne
Bjarne Stroustrup - http://www.research.att.com/~bs

Sandy McPherson

unread,
May 5, 1999, 3:00:00 AM5/5/99
to
John Potter wrote:

> sa...@bear.com wrote:
>
> : I do not use pointer where pass by value or by const reference is
> : preferable. On the other hand, I prefer pointers to non-const reference
> : unless it is the return value of an operator. It makes the intention

or an argument to an operator

class A &operator+=( class A &, const class B &);

Do you mean global functions, or member functions? Generally the only
thing a member function should change is 'this', which as we all know
is passed as a pointer. IMHO Changing something passed to a member
function is something which can normally be avoided.
If a member function is to produce something then it should normally
give this through the function return. Obvious exceptions are i/o
classes where a read buffer is passed to the input functions, and
other similar situations where it is more efficient to allow the
member function to copy into an existing object rather than have
it allocate a new object or return one by value. In situations
like this it is generally obvious what is happening from the name
of the member function.

class X
{
int member;
int Member() { return member; } //not so clear definition, but obvious
when used m = Member().
int getMember() { return member; } //clear definition, not so aesthetic
in use
void GetMember( int &m ) { m = member; } // yeuch!
void GetMember( int *m ) { *m = member; } // yeuch!
void GetData( void *buffer, size_t bytes ); // efficient for repetive
use
smartPointer<Message> nextMessage(); //clear name and obvious SAFE
behaviour
void nextMessage( Message & ); //clear name and obvious SAFE behaviour
Message *nextMessage(); //clear name and obvious behaviour, but
potential memory leak?
}

Many functions on the other hand WILL change one of their arguments
as it is their sole purpose in life to do so. Using pointers in this
situation
could highlights which parameter(s) is(are) going to be changed,
certainly useful if there are more than two or three parameters.
Of course this becomes academic if the thing being changed is an array
as a pointer to an array is a bit of a tautology!

>
> : clear at the time of call.

for instsance

sort<Container>( &container );

The use of a pointer is a bit of overkill...

>
>
> To you and to me; however, it is an implementation detail which I prefer
> not to see at the call site. I prefer a function whose name indicates
> what it is doing to my arguments. I want to have an interface which
> lets me use it, not makes me do something special to use it.

I think this is aquestion of preference and personal taste.
I like the pointer idea, when used with member functions as it
can highlight any side effects. e.g.

class X
{
void *peekData( int * ); // returns the number of bytes
}

--
Sandy Mcpherson, Nokia Mobile Phones
Mailto:Sandy.M...@nokia.com

Niklas Borson

unread,
May 5, 1999, 3:00:00 AM5/5/99
to
Sandy McPherson <Sandy.M...@nokia.com> wrote in message
news:372FF7EA...@nokia.com...

> John Potter wrote:
>
> > sa...@bear.com wrote:
> >
> > : I do not use pointer where pass by value or by const reference is
> > : preferable. On the other hand, I prefer pointers to non-const
reference
> > : unless it is the return value of an operator. It makes the intention
>
> [...]

>
> I think this is aquestion of preference and personal taste.
> I like the pointer idea, when used with member functions as it
> can highlight any side effects. e.g.
>
> class X
> {
> void *peekData( int * ); // returns the number of bytes
> }
>
> [...]

Perhaps I'm just contrary, but there are only two cases in which
I'd choose pointers over references for out parameters:

- for optional parameters, i.e., if null is permitted
- if the out parameter is an array

Declaring a required out parameter as a reference enforces the
non-null requirement, and eliminates the need for the function
to check for null.

It's true that pointers help make the "outness" of a parameter
clear at the call site, but coding conventions can help make
this clear for reference parameters too:

// At the declaration...
class X
{
bool get_property( // function name implies an out
const char* in_name, // in_ parameters come first
std::string& out_value // out_ parameters come last
);
}

// At the call site...
std::string value;
if (x.get_property("foo", value)) {
// is there any doubt that value holds the result?
}

A pointer is of course needed if you're passing an array as
an out parameter, but in such cases I would usually prefer to
pass a reference to a vector or similar object.

Michael Falcon-Gates

unread,
May 6, 1999, 3:00:00 AM5/6/99
to

Niklas Borson wrote in message <7gpv46$b...@news.dns.microsoft.com>...

>Perhaps I'm just contrary, but there are only two cases in which
>I'd choose pointers over references for out parameters:
>
> - for optional parameters, i.e., if null is permitted
> - if the out parameter is an array
>
>Declaring a required out parameter as a reference enforces the
>non-null requirement, and eliminates the need for the function
>to check for null.


I _wish_ people wouldn't keep thinking this; it isn't true. At my last major
contract, I had to track down two or three bugs that turned out to be
something like

void func(Foo& bar)
{
// try to use bar in some way
return;
}

{
Foo *baz = 0;
func(*baz);
}

Typically, the code with pointers had been written in version 1.0 by an old
C programmer, and the code with the references was written in version 2.0 by
a new hire. Both people were competent, it's just that the new guy had the
"references can't be null" idea stuck in his head. So, I ended up doing a
few more bug fixes in v3.0 than I would have otherwise.

ANYway, quit thinking that references can't be NULL; they can. It's always a
bug, but it is possible. Using references just makes it harder to CHECK for
nulls.

-m

Gerhard Menzl

unread,
May 6, 1999, 3:00:00 AM5/6/99
to
Niklas Borson wrote:

> It's true that pointers help make the "outness" of a parameter
> clear at the call site

Only if the value to be passed is of the type the pointer points to and
is thus preceded by the address operator. If the value is already
referred to by a pointer at the call site, the opposite is true, since
making the parameter a reference would require an extra asterisk.

Compare:

// f.h

void f (int* i);

// X.h

class X
{
//...
void g ();
int m;
}

// X.cpp

void X::g () { f (&m); }

versus:

// f.h

void f (int& i);

// X.h

class X
{
//...
void g ();
int* m;
}

// X.cpp

void X::g () { f (*m); }

Gerhard Menzl

Niklas Borson

unread,
May 7, 1999, 3:00:00 AM5/7/99
to
Gerhard Menzl <gerhar...@sea.ericsson.se> wrote in message
news:37316452...@sea.ericsson.se...

> Niklas Borson wrote:
>
> > It's true that pointers help make the "outness" of a parameter
> > clear at the call site
>
> Only if the value to be passed is of the type the pointer points to and
> is thus preceded by the address operator. If the value is already
> referred to by a pointer at the call site, the opposite is true, since
> making the parameter a reference would require an extra asterisk.
>
> [ example snipped ]

In the clause you cite above, I was merely making a concession
to those who favor pointers on the grounds that a call of the
form f(&x) makes it clearer that x is an out parameter than
would a call of the form f(x).

Read the rest of my message. My main point was that -- even
granting the above -- references are still better because it
makes passing a null pointer impossible and, further, that
the "outness" is still clear enough if you use reasonable
function names and a consistent ordering of parameters. If
anything, your argument strengthens my main point.

Nevertheless, I don't see how a call of the form f(*p) makes
p look more like an out parameter than f(p).

James...@dresdner-bank.com

unread,
May 7, 1999, 3:00:00 AM5/7/99
to
In article <FB7qD...@research.att.com>,
b...@research.att.com (Bjarne Stroustrup) wrote:

[...]


> Rather, people should have a basic understanding of cost factors so
> that they can focus on what matters for a given project. If you are
> refreshing a megapixel display, you have one set of concerns, if you
> are doing transactions across a LAN you have another set. Only if you

> have some basic understanding of the factors that affect your costs


> can you hope to spend your time and effort wisely. Some basic
> measurements early on in a project can be most valuable in avoiding
> debacles such as the one Scott described.

The debacle is only one if there is no easy fix. My own experience
would suggest that *any* worrying about such performance before getting
the basic encapsulation down right is premature. If the encapsulation
is correct, you can reimplement the critical parts (say, replacing deep
copy with reference counting, linear search with a hash table, etc.)
with only local modifications.

And I repeat: I'm not saying this from any theoretical basis, but from
concrete experience dealing with real performance problems.

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49 (069) 63 19 86 27

-----------== Posted via Deja News, The Discussion Network ==----------


http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Francis Glassborow

unread,
May 7, 1999, 3:00:00 AM5/7/99
to
In article <P_1Y2.390$O86....@news.uswest.net>, Michael Falcon-Gates
<mi...@uswest.net> writes

>{
>Foo *baz = 0;
>func(*baz);

No the problem is here. Anyone who dereferences a pointer without
checking that it is not null is asking for trouble. References have
nothing to do with it.

>}
>
>Typically, the code with pointers had been written in version 1.0 by an old
>C programmer, and the code with the references was written in version 2.0 by
>a new hire. Both people were competent, it's just that the new guy had the
>"references can't be null" idea stuck in his head. So, I ended up doing a
>few more bug fixes in v3.0 than I would have otherwise.
>
>ANYway, quit thinking that references can't be NULL; they can. It's always a
>bug, but it is possible. Using references just makes it harder to CHECK for
>nulls.

Francis Glassborow Journal Editor, Association of C & C++ Users


64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Scott Meyers

unread,
May 7, 1999, 3:00:00 AM5/7/99
to
On 6 May 1999 11:40:47 -0400, Michael Falcon-Gates wrote:
>
> Niklas Borson wrote in message <7gpv46$b...@news.dns.microsoft.com>...
> >Declaring a required out parameter as a reference enforces the
> >non-null requirement, and eliminates the need for the function
> >to check for null.
>
> I _wish_ people wouldn't keep thinking this; it isn't true. At my last major
> contract, I had to track down two or three bugs that turned out to be
> something like
>
> void func(Foo& bar)
> {
> // try to use bar in some way
> return;
> }
>
> {
> Foo *baz = 0;
> func(*baz);
> }
>
> Typically, the code with pointers had been written in version 1.0 by an old
> C programmer, and the code with the references was written in version 2.0 by
> a new hire. Both people were competent, it's just that the new guy had the
> "references can't be null" idea stuck in his head. So, I ended up doing a
> few more bug fixes in v3.0 than I would have otherwise.

The declaration of func implicitly asserts that bar refers to an object.
As you note, if this is not true, it's always a bug. Always. If somebody
passes a dereferenced null pointer, they are guilty of undefined behavior,
and it's their fault.

The alternative is that func is incorrectly declared. If callers of func
are supposed to be able to pass in something that might not refer to an
object, whoever declared func got it wrong. They should fix the
declaration.

> ANYway, quit thinking that references can't be NULL; they can. It's always a
> bug, but it is possible. Using references just makes it harder to CHECK for
> nulls.

Using a reference asserts that it's somebody else's responsibility to check
for nulls. If you have to check for "null references" in your software
(possibly because you're working with experienced C programmers who are
unfamiliar with the semantics of references), you are probably best off not
using references at all. In a C++ program with well-defined behavior,
references can NOT be null. If you test for "null references" in your
software, you are producing self-contradictory code, simultaneously
asserting that something must be there and then checking to make sure
anyway.

Scott

--
Scott Meyers, Ph.D. sme...@aristeia.com
Software Development Consultant http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

James...@dresdner-bank.com

unread,
May 8, 1999, 3:00:00 AM5/8/99
to
In article <P_1Y2.390$O86....@news.uswest.net>,

"Michael Falcon-Gates" <mi...@uswest.net> wrote:
>
> Niklas Borson wrote in message <7gpv46$b...@news.dns.microsoft.com>...
> >Perhaps I'm just contrary, but there are only two cases in which
> >I'd choose pointers over references for out parameters:
> >
> > - for optional parameters, i.e., if null is permitted
> > - if the out parameter is an array
> >
> >Declaring a required out parameter as a reference enforces the
> >non-null requirement, and eliminates the need for the function
> >to check for null.
>
> I _wish_ people wouldn't keep thinking this; it isn't true.

References can't be null, by definition. (Or rather, by the lack of a
definition of « null reference ».) References *can* dangle, and
references *can* be invalid.

> At my last major
> contract, I had to track down two or three bugs that turned out to be
> something like
>
> void func(Foo& bar)
> {
> // try to use bar in some way
> return;
> }
>
> {
> Foo *baz = 0;
> func(*baz);
> }
>
> Typically, the code with pointers had been written in version 1.0 by an old
> C programmer, and the code with the references was written in version 2.0 by
> a new hire. Both people were competent,

A C programmer who writes *baz without having first checked that baz is
not null is not competent. This has nothing to do with references. (Of
course, references may make it harder to find the error. But it would
be just as much an error here if func took Foo as a value parameter,
rather than a reference.

A good compiler will have an option to generate checks for null in such
cases.

> it's just that the new guy had the
> "references can't be null" idea stuck in his head. So, I ended up doing a
> few more bug fixes in v3.0 than I would have otherwise.
>

> Anyway, quit thinking that references can't be NULL; they can. It's always a


> bug, but it is possible. Using references just makes it harder to CHECK for
> nulls.

References can't be NULL, because NULL is a null pointer constant with
an integral type. References can't be null pointers, either, because a
null pointer is a pointer, not a reference. References *can* be
invalid, due to undefined behavior in the program.

The best way to think of references is as an alias, or another name, for
a value. Thus, in your example, if func took a value, rather than a
reference, it wouldn't occur to you (or anyone else) to assign *baz to
it without first being sure that baz is not null. The same thing goes
for a reference.

--
James Kanze mailto: James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49 (069) 63 19 86 27

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bernard A. Badger

unread,
May 8, 1999, 3:00:00 AM5/8/99
to

Michael Falcon-Gates <mi...@uswest.net> wrote in message
news:P_1Y2.390$O86....@news.uswest.net...

>
> Niklas Borson wrote in message <7gpv46$b...@news.dns.microsoft.com>...
> >Perhaps I'm just contrary, but there are only two cases in which
> >I'd choose pointers over references for out parameters:
> >
> > - for optional parameters, i.e., if null is permitted
> > - if the out parameter is an array
> >
> >Declaring a required out parameter as a reference enforces the
> >non-null requirement, and eliminates the need for the function
> >to check for null.
>
>
> I _wish_ people wouldn't keep thinking this; it isn't true. At my last

major
> contract, I had to track down two or three bugs that turned out to be
> something like
>
> void func(Foo& bar)
> {
> // try to use bar in some way
> return;
> }
>
> {
> Foo *baz = 0;
> func(*baz);
> }
>
> Typically, the code with pointers had been written in version 1.0 by an
old
> C programmer, and the code with the references was written in version 2.0
by
> a new hire. Both people were competent, it's just that the new guy had the

> "references can't be null" idea stuck in his head. So, I ended up doing a
> few more bug fixes in v3.0 than I would have otherwise.
>
> ANYway, quit thinking that references can't be NULL; they can. It's always

a
> bug, but it is possible. Using references just makes it harder to CHECK
for
> nulls.

It's not hard.
void func(Foo& bar)
{
if (&bar == NULL) // Here is the test!
return; // What to do? Return silently?!!! Or maybe
raise an exeception?


// try to use bar in some way
return;
}

If I recall correctly, what I have heard is that the call
func(*baz);
invokes undefined behaviour if (baz == NULL). Of course, the usual behavior
is probably to just
set the hidden pointer for bar to baz and then call the function. Thus it
"works" but has violated the
"references are non-null" rule.

Unfortunately, this seems to be a place where the Standard seems to offer a
guarantee to the
protect a programmer from a certain class of error (dereferencing a NULL)
but all it has really done
is to push the "logical" error outside the function (to the function call).
And since there is no guarantee
that the function call error will be caught and reported, there isn't really
any practical protection.

In my opinion, the "references are non-null" guarantee is "null and void".

On the other hand, you should not program _expecting_ to pass (*NULL) into a
reference, since a
compiler may refuse to compile such an erroneous program. At the least, it
is non-portable.

Gerhard Menzl

unread,
May 10, 1999, 3:00:00 AM5/10/99
to
Niklas Borson wrote:

> Read the rest of my message. My main point was that -- even
> granting the above -- references are still better because it
> makes passing a null pointer impossible and, further, that
> the "outness" is still clear enough if you use reasonable
> function names and a consistent ordering of parameters. If
> anything, your argument strengthens my main point.

Which was my intention. :-)

> Nevertheless, I don't see how a call of the form f(*p) makes
> p look more like an out parameter than f(p).

If there were exclusively value types being used at the caller's site, I
would buy the argument. But since there is no such guarantee, it does
not hold, I think.

Gerhard Menzl

Joerg Schaible

unread,
May 12, 1999, 3:00:00 AM5/12/99
to
>The debacle is only one if there is no easy fix. My own experience
>would suggest that *any* worrying about such performance before getting
>the basic encapsulation down right is premature. If the encapsulation
>is correct, you can reimplement the critical parts (say, replacing deep
>copy with reference counting, linear search with a hash table, etc.)
>with only local modifications.

Not always. Look at the Gof Lightweight pattern. If you have to deal with a
lot of data or take care of transfered data in an n-tier-app you should
probably think about performance while designing your solution. But agreed,
these are special cases. An experianced persons will automatically know
where they have their focus.

Greetings, Jörg
--
BTW: It is normally better to answer to the group! For direct mail reply
exchange the ".A@T." by "@"

Salters

unread,
May 20, 1999, 3:00:00 AM5/20/99
to
Michael Falcon-Gates wrote:

> Niklas Borson wrote in message <7gpv46$b...@news.dns.microsoft.com>...

> >Declaring a required out parameter as a reference enforces the


> >non-null requirement, and eliminates the need for the function
> >to check for null.
>
> I _wish_ people wouldn't keep thinking this; it isn't true. At my last major
> contract, I had to track down two or three bugs that turned out to be
> something like
>
> void func(Foo& bar)
> {
> // try to use bar in some way
> return;
> }
>
> {
> Foo *baz = 0;
> func(*baz);
> }
>
> Typically, the code with pointers had been written in version 1.0 by an old
> C programmer, and the code with the references was written in version 2.0 by
> a new hire. Both people were competent, it's just that the new guy had the
> "references can't be null" idea stuck in his head. So, I ended up doing a
> few more bug fixes in v3.0 than I would have otherwise.
>
> ANYway, quit thinking that references can't be NULL; they can. It's always a
> bug, but it is possible. Using references just makes it harder to CHECK for
> nulls.

NO!
Even a C programmer knows that the error occurs when you dereference
a NULL pointer. Using *baz - even as an argument to a function taking
a reference - is an error. Checking if &bar is NULL may save you on
your compiler, but this is not portable. Another compiler may cause
an error before this check, because references indeed can't be NULL.
Therefore : since you cannot check for NULL (at least not in a
portable way), you don't have to. You can check if you're
dereferencing a NULL pointer, and therefore, you should. And I
wouldn't call a programmer that dereferences NULL competent, whether
he is a C or C++ programmer.

What does this mean?

void func(Foo &bar)
{
// either &bar is non-NULL, or
// we have undefined behavior.
}

{
Foo *baz=0;
func (*baz);
// This could call func. It also could cause your OS to crash.
// No check inside func influences what your compiler does with
// *baz.
}

I guess it's time for v4.0

--
Michiel Salters
Michiel...@cmg.nl ATI-20
Consultant Software Engeneering

0 new messages