Returning by value or reference

M

mjm

Folks,

Stroustrup indicates that returning by value can be faster than
returning by reference but gives no details as to the size of the
returned object up to which this holds.

My question is up to which size m would you expect

vector<double> returns_by_value()
{
int m=...;
vector<double> x(m);
return x;
}


to be faster than


vector<double>& returns_by_reference()
{
int m=...;
vector<double>* x=new vector<double>(m);
return *x;
}
 
J

Jonathan Mcdougall

Stroustrup indicates that returning by value can be faster than
returning by reference

I can see no such case, but I may be mistaken.
My question is up to which size m would you expect

vector<double> returns_by_value()
{
int m=...;
vector<double> x(m);
return x;
}


to be faster than


vector<double>& returns_by_reference()
{
int m=...;
vector<double>* x=new vector<double>(m);
return *x;
}

It depends.

First, the return_by_reference() function is a bomb. The one who will
call it will have absolutly _no_ idea he has to delete the object.
You would be better doing something like

static vector<double> x(m);
return x;

But then all callers will be bound to the same object. This is not
good practice : you cannot return a reference to something you didn't
have at first.

Next, using the heap is almost always slower than using the stack, so
I would expect the second function to be slower in any case.

What`s more, the compiler could optimize the copy returned by
returns_by_value(), so you would end up only creating the object once,
and on the stack. This version will almost always be faster than the
second one.

Where did you see that? Maybe the context was different?


Jonathan
 
S

Shane Beasley

Stroustrup indicates that returning by value can be faster than
returning by reference but gives no details as to the size of the
returned object up to which this holds.
My question is up to which size m would you expect

vector<double> returns_by_value()
{
int m=...;
vector<double> x(m);
return x;
}

First, the efficiency of this function depends on the compiler. Some
perform what is called named return value optimization (NRVO), which
is related to the fact that your returned value has a name ("x").
However, many more compilers support unnamed return value optimization
(RVO), which has the additional requirement that the returned object
be an unnamed temporary:

vector<double> returns_by_value () {
int m=...;
to be faster than


vector<double>& returns_by_reference()
{
int m=...;
vector<double>* x=new vector<double>(m);
return *x;
}

Never return a reference to a dynamically-allocated object which the
caller is expected to clean up, not even as an example. You should
return an object by reference only if that object exists before the
function call, i.e., if it's global, static, or passed in by pointer
or reference (including the "this" pointer for member functions).

This is important, because your question is purely academic (read:
pointless) if you're not going to use an example of code you might
reasonably use in an actual program. In an actual program, the obvious
alternative to returning by value is to take a pointer or reference to
the object and modify it in-place, like so:

vector<double> &assigns_by_reference (vector<double> &x) {
int m=...;
x.assign(m, 0.0);
return x;
}

It's only fair to compare this real-world code to returns_by_value();
that is what I will do.

So, assigns_by_reference() requires that its argument be constructed
already, whereas returns_by_value() does not:

// (a)
vector<double> foo; // default-construct
assigns_by_reference(foo); // assign new value

// (b)
vector<double> bar = returns_by_value(); // construct w/ new value

If the compiler supports [N]RVO, (b) may actually be negligibly faster
than (a). [N]RVO allows the compiler to construct the returned value
*inside* "bar", thus eliminating (we call it "eliding") a copy
constructor or destructor call, as well as any code dealing with
returning a value (since the returned value is used directly as
"bar").

HOWEVER....

If the compiler doesn't support [N]RVO, then (b) may involve an
additional copy construction and destruction of the temporary. The
same is true if the code involves assignment, as [N]RVO applies only
to construction:

// (c)
vector<double> bar; // default-construct
bar = returns_by_value(); // assign new value

As you guessed, exactly how slow this is depends on the size of the
returned vector. Unfortunately, the actual "m" at which (b) (without
[N]RVO) or (c) becomes slower than (a) is dependent on the compiler.
The only real way to find out is to run it with various values for "m"
and see what happens.

Note: You can replace (c) with this:

// (d)
vector<double> foo; // default-construct
returns_by_value().swap(foo); // swap old and new values

This should be roughly the same as (a), and about as fast, too.

Hope that helps!

- Shane
 
J

Jeff Williams

mjm said:
Folks,

Stroustrup indicates that returning by value can be faster than
returning by reference but gives no details as to the size of the
returned object up to which this holds.

I was curious of this myself a while back so I ran a test of my own.

I don't have exact test results any more but you can recreate. What I did
was create a struct:

struct fvector2
{
float c[2];
};

struct fvector3
{
float c[3];
};


First I executed a function returning each of these by value and then by
ref. I timed called them around 1million times each.

My result was the fvector2 was faster returning by value and fvector3 was
faster by reference. I had all -O3 on GCC 3.2.3, your mileage may vary.

Jeff
 
E

E. Robert Tisdale

mjm said:
Stroustrup indicates that

Please cite and quote Stroustrup.
returning by value can be faster
than returning by reference
but gives no details as to
the size of the returned object up to which this holds.

My question is up to which size m would you expect

vector<double> returns_by_value(int m) {
vector<double> x(m);
return x;
}

to be faster than

vector<double>& {
vector<double>* x = new vector<double>(m);
return *x;
}

You would be obliged to write

vector<double>& r = returns_by_reference(int m);
// use r
delete &r;

This is dangerous because you would create a memory leak
if the thread of execution failed to pass through the delete.
You would barely notice (you probably couldn't measure)
the overhead for the extra new and delete for a single invocation.
You would probably need to embed it in vary a tight loop
over small vectors.
 
M

mjm

Thanks for all the valuable information.
My vectors typically have dimension 20-80 doubles.

I understand the deallocation problem.
Actually in my own program the question how to return does not occur
in critical loops so I could and probably should return by value.

There is a nasty problem as follows: the constructors of the objects
(vectors, matrices) themselves allocate memory on the heap. So you
cannot escape heap allocation even if you return by value.

Is there any way to get around this if you want to dynamically
allocate arrays possibly ragged (triangular matrices for example).

I understand that you could avoid returning anything and instead write
into existing objects. But this makes really ugly C-like code.

The section of Stroustrup (anniversary edition) is 11.6, p283.
There is more than one page of information on the problem so I won't
provide a detailed quote.
 
B

Bjarne Stroustrup

Thanks for all the valuable information.
My vectors typically have dimension 20-80 doubles.
...
The section of Stroustrup (anniversary edition) is 11.6, p283.
There is more than one page of information on the problem so I won't
provide a detailed quote.

A better reference is TC++PL3 22.4.7. That's the technique for
eliminating spurious temporaries used by most modern vector/matrix
libraries (see my C++ page and/or the libraries FAQ for examples of
such libraries).

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

David T. Croft

Returning by reference may involve some bookkeeping. If reference counting
is done than I'd think returning by value is faster for pointer-sized values
like long, Int32, etc. Does this concern .NET?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top