Returning by value or reference

Discussion in 'C++' started by mjm, Aug 8, 2003.

  1. mjm

    mjm Guest

    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;
    }
     
    mjm, Aug 8, 2003
    #1
    1. Advertising

  2. >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
     
    Jonathan Mcdougall, Aug 8, 2003
    #2
    1. Advertising

  3. (mjm) wrote in message news:<>...

    > 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=...;
    return vector<double>(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
     
    Shane Beasley, Aug 8, 2003
    #3
  4. "mjm" <> wrote in message
    news:...
    > 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
     
    Jeff Williams, Aug 8, 2003
    #4
  5. mjm wrote:

    > 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.
     
    E. Robert Tisdale, Aug 8, 2003
    #5
  6. mjm

    mjm Guest

    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.
     
    mjm, Aug 9, 2003
    #6
  7. (mjm) wrote in message news:<>...
    > 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
     
    Bjarne Stroustrup, Aug 9, 2003
    #7
  8. mjm

    mjm Guest

    Thanks for the response and C++.

    > 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
     
    mjm, Aug 9, 2003
    #8
  9. 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?

    "mjm" <> wrote in message
    news:...
    > 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;
    > }
     
    David T. Croft, Aug 10, 2003
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sree
    Replies:
    3
    Views:
    15,455
  2. Replies:
    11
    Views:
    671
    Christos Georgiou
    May 2, 2006
  3. JustMe
    Replies:
    1
    Views:
    175
    Tassilo v. Parseval
    Aug 29, 2003
  4. wana
    Replies:
    1
    Views:
    79
    Gunnar Hjalmarsson
    Sep 29, 2004
  5. Replies:
    7
    Views:
    443
    James Kanze
    Jul 30, 2012
Loading...

Share This Page