pre return optimization

Discussion in 'C++' started by terminator(jam), Oct 9, 2007.

  1. consider:

    struct memory_pig{//a really large type:

    memory_pig(){
    std::cout<<"mem pig default\n";
    //etc...
    };

    memory_pig(memory_pig const&){
    std::cout<<"mem pig copy\n";
    //etc...
    };

    ~memory_pig(){
    std::cout<<"mem pig finish\n";
    //etc...
    };

    //etc...

    };///struct memory_pig

    memory_pig foo(){
    memory_pig result;
    result=something;
    //etc...
    result=something_else;
    return result;
    };

    any time 'foo' is called the output will contain the following
    sequence:


    mem pig default
    mem pig copy
    mem pig finish

    the last line of output may repeat based on how the result is
    stored(rvo) or not.
    So,two objects of a large type will be constructed and at least one is
    destructed on every call to 'foo' in PASCAL you can write:

    function foo:memory_pig
    begin
    foo:=something;
    {etc...}
    foo:=somthing_else;
    end

    that is you can refrence the returned object inside the function and
    decrease the overhead for copying large objects.C++ lacks such syntax
    and IMHO we should be able to mark the result object as referencing
    the actual return so that there is no need for the extra copy
    construction;this is espesifically beneficall when dealing with
    operator definitions .We can declare the return itself as an object.I
    suggest the following syntax:

    class ret_type funxn (paramlist){
    ret_type return /*optional:*/(initiallizer params);

    //etc...

    if(false)return;//return like void functions

    //return something;//error:named return accepts no param.

    return=something;//ok;
    return=something_else;
    return.member_funxn();
    another_funcxn(return);

    };

    Provided that 'return' is declared as an object { whenever you use the
    'return' keyword inside pharantesis or accompanied via an operator ?
    the return object is referenced :eek:therwise a parameterless 'return'
    returns to the caller.
    The return object is an lvalue inside the function and can be aliased
    for readability:

    memory_pig foo(){
    memory_pig return , & result=return;
    result=something;
    //etc...
    result=something_else;
    };

    and whenever 'foo' is called the output looks like this:

    mem pig default

    the unnessesary copy/move has vanished and the destruction of
    temporary depends on what you do with it.

    regards,
    FM.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator(jam), Oct 9, 2007
    #1
    1. Advertising

  2. On 2007-10-09 17:56, terminator(jam) wrote:
    > consider:
    >
    > struct memory_pig{//a really large type:
    >
    > memory_pig(){
    > std::cout<<"mem pig default\n";
    > //etc...
    > };
    >
    > memory_pig(memory_pig const&){
    > std::cout<<"mem pig copy\n";
    > //etc...
    > };
    >
    > ~memory_pig(){
    > std::cout<<"mem pig finish\n";
    > //etc...
    > };
    >
    > //etc...
    >
    > };///struct memory_pig
    >
    > memory_pig foo(){
    > memory_pig result;
    > result=something;


    This should be

    memory_pig result = something;

    or

    memory_pig result(something);


    > //etc...
    > result=something_else;
    > return result;
    > };
    >
    > any time 'foo' is called the output will contain the following
    > sequence:
    >
    >
    > mem pig default
    > mem pig copy
    > mem pig finish
    >
    > the last line of output may repeat based on how the result is
    > stored(rvo) or not.



    With some smart coding you can transform the above to:

    memory_pig foo(){
    // ...
    return memory_pig(something, something_else);
    }

    And with RVO you should only have one constructor call.

    > So,two objects of a large type will be constructed and at least one is
    > destructed on every call to 'foo' in PASCAL you can write:
    >
    > function foo:memory_pig
    > begin
    > foo:=something;
    > {etc...}
    > foo:=somthing_else;
    > end
    >
    > that is you can refrence the returned object inside the function and
    > decrease the overhead for copying large objects.


    I am not familiar with what that exactly means in Pascal, is it
    something similar to

    void foo(memory_pig& ret) {
    // ...
    ret = something;
    // ...
    ret = something_else;
    }

    Another thing that you seem to forget is that for large objects the
    assignment is not cheap either, sometimes even more costly than a
    constructor call.

    --
    Erik Wikström
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 9, 2007
    #2
    1. Advertising

  3. terminator(jam)

    James Kanze Guest

    terminator(jam) wrote:
    > consider:


    > struct memory_pig{//a really large type:


    > memory_pig(){
    > std::cout<<"mem pig default\n";
    > //etc...
    > };


    > memory_pig(memory_pig const&){
    > std::cout<<"mem pig copy\n";
    > //etc...
    > };


    > ~memory_pig(){
    > std::cout<<"mem pig finish\n";
    > //etc...
    > };


    > //etc...


    > };///struct memory_pig


    > memory_pig foo(){
    > memory_pig result;
    > result=something;
    > //etc...
    > result=something_else;
    > return result;
    > };


    > any time 'foo' is called the output will contain the following
    > sequence:


    > mem pig default
    > mem pig copy
    > mem pig finish


    I'm not sure I understand. Even in a worst case scenario, I
    can't see where there would be more than two copies of
    memory_pig in memory at a given time. Given the explicit
    authorization of NRVO by the standard, I would expect that
    in most implementations, there is never more than one.

    > the last line of output may repeat based on how the result
    > is stored(rvo) or not. So,two objects of a large type
    > will be constructed and at least one is destructed on
    > every call to 'foo' in PASCAL you can write:


    > function foo:memory_pig
    > begin
    > foo:=something;
    > {etc...}
    > foo:=somthing_else;
    > end


    Not can, in Pascal, you have to write it that way.

    It basically comes out to the same thing: the Pascal
    compiler generates a local variable with the name of foo for
    the return; if the name foo is used on the left hand side of
    an assignment, it refers to the local variable, and if it is
    used on the right hand side, it refers to the function.

    And of course, in C++, unlike in Pascal, you can return an
    expression directly, without first assigning it to a local
    variable.

    > that is you can refrence the returned object inside the
    > function and decrease the overhead for copying large
    > objects. C++ lacks such syntax and IMHO we should be able
    > to mark the result object as referencing the actual return
    > so that there is no need for the extra copy construction;
    > this is espesifically beneficall when dealing with
    > operator definitions.


    The current C++ standard leaves this up to the
    implementation, but explicitly allows it. The situation in
    C++ is more complicated than in Pascal, however, since you
    can return from anywhere, e.g.:

    memory_pig
    foo()
    {
    memory_pig result ;
    // ...
    if ( someCondition ) {
    memory_pig aDifferentResult ;
    // ...
    return aDifferentResult ;
    }
    // ...
    return result ;
    }

    Whether this is a feature or a defect could be debated, but
    it certainly cannot be removed from the language without
    breaking a considerable amount of existing code.

    --
    James Kanze (GABI Software) mailto:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Oct 10, 2007
    #3
  4. terminator(jam)

    terminator Guest

    On Oct 10, 11:18 am, James Kanze <> wrote:
    > terminator(jam) wrote:
    > > consider:
    > > struct memory_pig{//a really large type:
    > > memory_pig(){
    > > std::cout<<"mem pig default\n";
    > > //etc...
    > > };
    > > memory_pig(memory_pig const&){
    > > std::cout<<"mem pig copy\n";
    > > //etc...
    > > };
    > > ~memory_pig(){
    > > std::cout<<"mem pig finish\n";
    > > //etc...
    > > };
    > > //etc...
    > > };///struct memory_pig
    > > memory_pig foo(){
    > > memory_pig result;
    > > result=something;
    > > //etc...
    > > result=something_else;
    > > return result;
    > > };
    > > any time 'foo' is called the output will contain the following
    > > sequence:
    > > mem pig default
    > > mem pig copy
    > > mem pig finish

    >
    > I'm not sure I understand. Even in a worst case scenario, I
    > can't see where there would be more than two copies of
    > memory_pig in memory at a given time. Given the explicit
    > authorization of NRVO by the standard, I would expect that
    > in most implementations, there is never more than one.
    >
    > > the last line of output may repeat based on how the result
    > > is stored(rvo) or not. So,two objects of a large type
    > > will be constructed and at least one is destructed on
    > > every call to 'foo' in PASCAL you can write:
    > > function foo:memory_pig
    > > begin
    > > foo:=something;
    > > {etc...}
    > > foo:=somthing_else;
    > > end

    >
    > Not can, in Pascal, you have to write it that way.
    >
    > It basically comes out to the same thing: the Pascal
    > compiler generates a local variable with the name of foo for
    > the return; if the name foo is used on the left hand side of
    > an assignment, it refers to the local variable, and if it is
    > used on the right hand side, it refers to the function.


    C++ generates an unnamed local variable which is not destructed by the
    function ,this variable is constructed right before destruction of
    other automatic variable which means it does not overwrite any
    existing automatic variable ,So it has to be allocated in a separate
    portion of [stack]memory.I just think it is posible to guess where the
    'return' value is to be placed right at the beginning of the
    function,so it can be constructed and refrenced before returning.
    the choice between two modes of returning(name vs traditional) is left
    to the programmer.

    >
    > And of course, in C++, unlike in Pascal, you can return an
    > expression directly, without first assigning it to a local
    > variable.
    >
    > > that is you can refrence the returned object inside the
    > > function and decrease the overhead for copying large
    > > objects. C++ lacks such syntax and IMHO we should be able
    > > to mark the result object as referencing the actual return
    > > so that there is no need for the extra copy construction;
    > > this is espesifically beneficall when dealing with
    > > operator definitions.

    >
    > The current C++ standard leaves this up to the
    > implementation, but explicitly allows it. The situation in
    > C++ is more complicated than in Pascal, however, since you
    > can return from anywhere, e.g.:
    >
    > memory_pig
    > foo()
    > {
    > memory_pig result ;
    > // ...
    > if ( someCondition ) {
    > memory_pig aDifferentResult ;
    > // ...
    > return aDifferentResult ;
    > }
    > // ...
    > return result ;
    > }
    >
    > Whether this is a feature or a defect could be debated, but
    > it certainly cannot be removed from the language without
    > breaking a considerable amount of existing code.
    >


    I believe it is a feature for many cases and a defect in many other
    cases (operator functions)and I do not mean to remove anything from
    the existing language.I just want to make it possible avoid the second
    construction that happens at the end of a function (either move or
    copy).

    regards,
    FM.
    terminator, Oct 11, 2007
    #4
  5. terminator(jam)

    terminator Guest

    On Oct 9, 7:08 pm, Erik Wikström <> wrote:
    > On 2007-10-09 17:56, terminator(jam) wrote:
    >
    >
    >
    >
    >
    > > consider:

    >
    > > struct memory_pig{//a really large type:

    >
    > > memory_pig(){
    > > std::cout<<"mem pig default\n";
    > > //etc...
    > > };

    >
    > > memory_pig(memory_pig const&){
    > > std::cout<<"mem pig copy\n";
    > > //etc...
    > > };

    >
    > > ~memory_pig(){
    > > std::cout<<"mem pig finish\n";
    > > //etc...
    > > };

    >
    > > //etc...

    >
    > > };///struct memory_pig

    >
    > > memory_pig foo(){
    > > memory_pig result;
    > > result=something;

    >
    > This should be
    >
    > memory_pig result = something;
    >
    > or
    >
    > memory_pig result(something);
    >
    > > //etc...
    > > result=something_else;
    > > return result;
    > > };

    >
    > > any time 'foo' is called the output will contain the following
    > > sequence:

    >
    > > mem pig default
    > > mem pig copy
    > > mem pig finish

    >
    > > the last line of output may repeat based on how the result is
    > > stored(rvo) or not.

    >
    > With some smart coding you can transform the above to:
    >
    > memory_pig foo(){
    > // ...
    > return memory_pig(something, something_else);
    >
    > }
    >
    > And with RVO you should only have one constructor call.
    >
    > > So,two objects of a large type will be constructed and at least one is
    > > destructed on every call to 'foo' in PASCAL you can write:

    >
    > > function foo:memory_pig
    > > begin
    > > foo:=something;
    > > {etc...}
    > > foo:=somthing_else;
    > > end

    >
    > > that is you can refrence the returned object inside the function and
    > > decrease the overhead for copying large objects.

    >
    > I am not familiar with what that exactly means in Pascal, is it
    > something similar to
    >
    > void foo(memory_pig& ret) {
    > // ...
    > ret = something;
    > // ...
    > ret = something_else;
    >
    > }
    >


    yes,but just fancy yourself definning an operator instead of a normal
    function.If operators could be declared as objects rather than
    functions ,one could declare them as instances of functor classes and
    solve the problem.BTW it looks very hard to parse since this would
    need major changes in semantics;I think working on the return will be
    easier.

    > Another thing that you seem to forget is that for large objects the
    > assignment is not cheap either, sometimes even more costly than a
    > constructor call.


    that was just an illustration replace it with compound
    assignment(+= , ...) or any other sort of referencing( member/static
    function,...) . I just want to return the object which is processed in
    the function,not a copy/moved object .

    regards,
    FM.
    terminator, Oct 11, 2007
    #5
  6. On 2007-10-11 09:56, terminator wrote:
    > On Oct 9, 7:08 pm, Erik Wikström <> wrote:
    >> On 2007-10-09 17:56, terminator(jam) wrote:
    >>
    >> > consider:

    >>
    >> > struct memory_pig{//a really large type:

    >>
    >> > memory_pig(){
    >> > std::cout<<"mem pig default\n";
    >> > //etc...
    >> > };

    >>
    >> > memory_pig(memory_pig const&){
    >> > std::cout<<"mem pig copy\n";
    >> > //etc...
    >> > };

    >>
    >> > ~memory_pig(){
    >> > std::cout<<"mem pig finish\n";
    >> > //etc...
    >> > };

    >>
    >> > //etc...

    >>
    >> > };///struct memory_pig

    >>
    >> > memory_pig foo(){
    >> > memory_pig result;
    >> > result=something;

    >>
    >> This should be
    >>
    >> memory_pig result = something;
    >>
    >> or
    >>
    >> memory_pig result(something);
    >>
    >> > //etc...
    >> > result=something_else;
    >> > return result;
    >> > };

    >>
    >> > any time 'foo' is called the output will contain the following
    >> > sequence:

    >>
    >> > mem pig default
    >> > mem pig copy
    >> > mem pig finish


    >> > So,two objects of a large type will be constructed and at least one is
    >> > destructed on every call to 'foo' in PASCAL you can write:

    >>
    >> > function foo:memory_pig
    >> > begin
    >> > foo:=something;
    >> > {etc...}
    >> > foo:=somthing_else;
    >> > end

    >>
    >> > that is you can refrence the returned object inside the function and
    >> > decrease the overhead for copying large objects.

    >>
    >> I am not familiar with what that exactly means in Pascal, is it
    >> something similar to
    >>
    >> void foo(memory_pig& ret) {
    >> // ...
    >> ret = something;
    >> // ...
    >> ret = something_else;
    >>
    >> }
    >>

    >
    > yes,but just fancy yourself definning an operator instead of a normal
    > function.If operators could be declared as objects rather than
    > functions ,one could declare them as instances of functor classes and
    > solve the problem.


    In C++ operators and functions are the same thing, operators just have a
    fancier syntax. I still do not see the problem, please explain in more
    detail.

    > BTW it looks very hard to parse since this would
    > need major changes in semantics;I think working on the return will be
    > easier.


    I do not know how hard the above is to parse but all C++ compilers that
    I know of can do it, it is very standard C++.

    --
    Erik Wikström
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 11, 2007
    #6
  7. terminator(jam)

    terminator Guest

    On Oct 11, 12:01 pm, Erik Wikström <> wrote:
    > On 2007-10-11 09:56, terminator wrote:
    >
    >
    >
    >
    >
    > > On Oct 9, 7:08 pm, Erik Wikström <> wrote:
    > >> On 2007-10-09 17:56, terminator(jam) wrote:

    >
    > >> > consider:

    >
    > >> > struct memory_pig{//a really large type:

    >
    > >> > memory_pig(){
    > >> > std::cout<<"mem pig default\n";
    > >> > //etc...
    > >> > };

    >
    > >> > memory_pig(memory_pig const&){
    > >> > std::cout<<"mem pig copy\n";
    > >> > //etc...
    > >> > };

    >
    > >> > ~memory_pig(){
    > >> > std::cout<<"mem pig finish\n";
    > >> > //etc...
    > >> > };

    >
    > >> > //etc...

    >
    > >> > };///struct memory_pig

    >
    > >> > memory_pig foo(){
    > >> > memory_pig result;
    > >> > result=something;

    >
    > >> This should be

    >
    > >> memory_pig result = something;

    >
    > >> or

    >
    > >> memory_pig result(something);

    >
    > >> > //etc...
    > >> > result=something_else;
    > >> > return result;
    > >> > };

    >
    > >> > any time 'foo' is called the output will contain the following
    > >> > sequence:

    >
    > >> > mem pig default
    > >> > mem pig copy
    > >> > mem pig finish
    > >> > So,two objects of a large type will be constructed and at least one is
    > >> > destructed on every call to 'foo' in PASCAL you can write:

    >
    > >> > function foo:memory_pig
    > >> > begin
    > >> > foo:=something;
    > >> > {etc...}
    > >> > foo:=somthing_else;
    > >> > end

    >
    > >> > that is you can refrence the returned object inside the function and
    > >> > decrease the overhead for copying large objects.

    >
    > >> I am not familiar with what that exactly means in Pascal, is it
    > >> something similar to

    >
    > >> void foo(memory_pig& ret) {
    > >> // ...
    > >> ret = something;
    > >> // ...
    > >> ret = something_else;

    >
    > >> }

    >
    > > yes,but just fancy yourself definning an operator instead of a normal
    > > function.If operators could be declared as objects rather than
    > > functions ,one could declare them as instances of functor classes and
    > > solve the problem.

    >
    > In C++ operators and functions are the same thing, operators just have a
    > fancier syntax. I still do not see the problem, please explain in more
    > detail.
    >


    **I certainly do disaggree.** operators follow a dedicated syntax ,you
    cannot change a binary operator to a void return ternary one:

    void operator+(const A&,const A&,A&);//syntax error.

    while you can choose either of the following:

    void foo(A&);//your suggestion
    A foo();//original context

    So since the operators generally do return a value ,I prefer to work
    on the return value itself ,not on an object which is going to be
    copied in the return location:

    class A A::eek:perator-(){
    A return(initializer_params);
    A& result=return;
    modify(result);
    if(some_circumstances())
    return;//end of op-() return to caller
    change(result);
    DoWhatever(result);
    };//end of op-() return to caller

    > > BTW it looks very hard to parse since this would
    > > need major changes in semantics;I think working on the return will be
    > > easier.

    >
    > I do not know how hard the above is to parse but all C++ compilers that
    > I know of can do it, it is very standard C++.


    I wrote:

    > If operators could be declared as objects rather than
    > functions ,one could declare them as instances of functor classes and
    > solve the problem.BTW it looks very hard to parse since this would
    > need major changes in semantics;I think working on the return will be
    > easier.


    that meant to treat operators as objects(not functions),which was a
    stupid idea.

    regards,
    FM.
    terminator, Oct 12, 2007
    #7
  8. On 2007-10-12 15:58, terminator wrote:
    > On Oct 11, 12:01 pm, Erik Wikström <> wrote:
    >> On 2007-10-11 09:56, terminator wrote:
    >>
    >>
    >>
    >>
    >>
    >> > On Oct 9, 7:08 pm, Erik Wikström <> wrote:
    >> >> On 2007-10-09 17:56, terminator(jam) wrote:

    >>
    >> >> > consider:

    >>
    >> >> > struct memory_pig{//a really large type:

    >>
    >> >> > memory_pig(){
    >> >> > std::cout<<"mem pig default\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > memory_pig(memory_pig const&){
    >> >> > std::cout<<"mem pig copy\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > ~memory_pig(){
    >> >> > std::cout<<"mem pig finish\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > //etc...

    >>
    >> >> > };///struct memory_pig

    >>
    >> >> > memory_pig foo(){
    >> >> > memory_pig result;
    >> >> > result=something;

    >>
    >> >> This should be

    >>
    >> >> memory_pig result = something;

    >>
    >> >> or

    >>
    >> >> memory_pig result(something);

    >>
    >> >> > //etc...
    >> >> > result=something_else;
    >> >> > return result;
    >> >> > };

    >>
    >> >> > any time 'foo' is called the output will contain the following
    >> >> > sequence:

    >>
    >> >> > mem pig default
    >> >> > mem pig copy
    >> >> > mem pig finish
    >> >> > So,two objects of a large type will be constructed and at least one is
    >> >> > destructed on every call to 'foo' in PASCAL you can write:

    >>
    >> >> > function foo:memory_pig
    >> >> > begin
    >> >> > foo:=something;
    >> >> > {etc...}
    >> >> > foo:=somthing_else;
    >> >> > end

    >>
    >> >> > that is you can refrence the returned object inside the function and
    >> >> > decrease the overhead for copying large objects.

    >>
    >> >> I am not familiar with what that exactly means in Pascal, is it
    >> >> something similar to

    >>
    >> >> void foo(memory_pig& ret) {
    >> >> // ...
    >> >> ret = something;
    >> >> // ...
    >> >> ret = something_else;

    >>
    >> >> }

    >>
    >> > yes,but just fancy yourself definning an operator instead of a normal
    >> > function.If operators could be declared as objects rather than
    >> > functions ,one could declare them as instances of functor classes and
    >> > solve the problem.

    >>
    >> In C++ operators and functions are the same thing, operators just have a
    >> fancier syntax. I still do not see the problem, please explain in more
    >> detail.
    >>

    >
    > **I certainly do disaggree.** operators follow a dedicated syntax ,you
    > cannot change a binary operator to a void return ternary one:


    What I meant was that overloaded operators are just special functions.
    While they do have special syntax, special precedence rules, and only a
    few allowed forms, when it comes to the generated code it is just like
    any other function call. Thus they are only functions with fancier syntax.

    >> > BTW it looks very hard to parse since this would
    >> > need major changes in semantics;I think working on the return will be
    >> > easier.

    >>
    >> I do not know how hard the above is to parse but all C++ compilers that
    >> I know of can do it, it is very standard C++.

    >
    > I wrote:
    >
    >> If operators could be declared as objects rather than
    >> functions ,one could declare them as instances of functor classes and
    >> solve the problem.BTW it looks very hard to parse since this would
    >> need major changes in semantics;I think working on the return will be
    >> easier.

    >
    > that meant to treat operators as objects(not functions),which was a
    > stupid idea.


    If you look at your post that I replied to it looked like you were
    referring to the piece of code that I wrote, which is why I replied as I
    did.

    --
    Erik Wikström
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 12, 2007
    #8
  9. terminator(jam)

    Greg Herlihy Guest

    On Oct 9, 8:56 am, "terminator(jam)" <> wrote:
    > consider:
    >
    > struct memory_pig{//a really large type:
    >
    > memory_pig(){
    > std::cout<<"mem pig default\n";
    > //etc...
    > };
    >
    > memory_pig(memory_pig const&){
    > std::cout<<"mem pig copy\n";
    > //etc...
    > };
    >
    > ~memory_pig(){
    > std::cout<<"mem pig finish\n";
    > //etc...
    > };
    >
    > //etc...
    >
    > };///struct memory_pig
    >
    > memory_pig foo(){
    > memory_pig result;
    > result=something;
    > //etc...
    > result=something_else;
    > return result;
    >
    > };
    >
    > any time 'foo' is called the output will contain the following
    > sequence:
    >
    > mem pig default
    > mem pig copy
    > mem pig finish
    >
    > the last line of output may repeat based on how the result is
    > stored(rvo) or not.
    > So,two objects of a large type will be constructed and at least one is
    > destructed on every call to 'foo'


    The C++ Standard already allows the "(Named) Return Value
    Optimization" (NRVO or RVO for short). The optimization allows (under
    certain conditions) the compiler to construct the result of a function
    call "in place" - that is, directly in the object initialized with the
    function call result.

    So, to take advantage of this optimization, a program should use the
    result of a function call to initialize an object, instead of
    assigning the function result to an existing object.

    For example:

    #include <iostream>

    using std::cout;

    struct memory_pig
    { // a really large type:
    memory_pig()
    {
    cout << "mem pig default\n";
    }
    memory_pig(memory_pig const&)
    {
    cout << "mem pig copy\n";
    }
    ~memory_pig()
    {
    cout << "mem pig finish\n";
    }
    };

    memory_pig foo()
    {
    memory_pig result;
    // ...
    return result;
    }

    int main()
    {
    memory_pig m1 = foo();
    memory_pig m2 = foo();
    memory_pig m3 = foo();
    }

    I compiled the above program twice, once with and once without NRVO.
    The output of both programs is shown in the two columns below. As this
    comparison shows, NRVO can be a particular effective optimization -
    even for a small C++ program like the one used in this example.

    Program Output

    Without NRVO: With NRVO:

    mem pig default mem pig default
    mem pig copy mem pig default
    mem pig finish mem pig default
    mem pig copy mem pig finish
    mem pig finish mem pig finish
    mem pig default mem pig finish
    mem pig copy
    mem pig finish
    mem pig copy
    mem pig finish
    mem pig default
    mem pig copy
    mem pig finish
    mem pig copy
    mem pig finish
    mem pig finish
    mem pig finish
    mem pig finish

    Greg


    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    Greg Herlihy, Oct 14, 2007
    #9
  10. On 2007-10-14 21:19, terminator wrote:
    > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:
    >>
    >>
    >>
    >>
    >>
    >> > consider:

    >>
    >> > struct memory_pig{//a really large type:

    >>
    >> > memory_pig(){
    >> > std::cout<<"mem pig default\n";
    >> > //etc...
    >> > };

    >>
    >> > memory_pig(memory_pig const&){
    >> > std::cout<<"mem pig copy\n";
    >> > //etc...
    >> > };

    >>
    >> > ~memory_pig(){
    >> > std::cout<<"mem pig finish\n";
    >> > //etc...
    >> > };

    >>
    >> > //etc...

    >>
    >> > };///struct memory_pig

    >>
    >> > memory_pig foo(){
    >> > memory_pig result;
    >> > result=something;
    >> > //etc...
    >> > result=something_else;
    >> > return result;

    >>
    >> > };

    >>
    >> > any time 'foo' is called the output will contain the following
    >> > sequence:

    >>
    >> > mem pig default
    >> > mem pig copy
    >> > mem pig finish

    >>
    >> > the last line of output may repeat based on how the result is
    >> > stored(rvo) or not.
    >> > So,two objects of a large type will be constructed and at least one is
    >> > destructed on every call to 'foo'

    >>
    >> The C++ Standard already allows the "(Named) Return Value
    >> Optimization" (NRVO or RVO for short). The optimization allows (under
    >> certain conditions) the compiler to construct the result of a function
    >> call "in place" - that is, directly in the object initialized with the
    >> function call result.
    >>
    >> So, to take advantage of this optimization, a program should use the
    >> result of a function call to initialize an object, instead of
    >> assigning the function result to an existing object.
    >>
    >> For example:
    >>
    >> #include <iostream>
    >>
    >> using std::cout;
    >>
    >> struct memory_pig
    >> { // a really large type:
    >> memory_pig()
    >> {
    >> cout << "mem pig default\n";
    >> }
    >> memory_pig(memory_pig const&)
    >> {
    >> cout << "mem pig copy\n";
    >> }
    >> ~memory_pig()
    >> {
    >> cout << "mem pig finish\n";
    >> }
    >> };
    >>
    >> memory_pig foo()
    >> {
    >> memory_pig result;
    >> // ...
    >> return result;
    >> }
    >>
    >> int main()
    >> {
    >> memory_pig m1 = foo();
    >> memory_pig m2 = foo();
    >> memory_pig m3 = foo();
    >> }
    >>
    >> I compiled the above program twice, once with and once without NRVO.
    >> The output of both programs is shown in the two columns below. As this
    >> comparison shows, NRVO can be a particular effective optimization -
    >> even for a small C++ program like the one used in this example.


    >>

    > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    > standard behavoir or unspecified?
    > At least my compiler does not perform the NRVO.


    The standard allows this optimisation, but it does not require it (see
    section 12.8 paragraph 15). I think that most newer compilers support
    it, but you might have to turn up the optimisation level a bit.

    One important thing to remember is that when NRVO is in use the
    destructor will not be called for the local object, nor will the copy-
    constructor for the non-local object (m1 to m3 in the code above) be
    called. Thus you should not depend on any side effects that the copy-
    constructor or destructor might have.

    --
    Erik Wikström

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 14, 2007
    #10
  11. terminator(jam)

    terminator Guest

    On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    > On Oct 9, 8:56 am, "terminator(jam)" <> wrote:
    >
    >
    >
    >
    >
    > > consider:

    >
    > > struct memory_pig{//a really large type:

    >
    > > memory_pig(){
    > > std::cout<<"mem pig default\n";
    > > //etc...
    > > };

    >
    > > memory_pig(memory_pig const&){
    > > std::cout<<"mem pig copy\n";
    > > //etc...
    > > };

    >
    > > ~memory_pig(){
    > > std::cout<<"mem pig finish\n";
    > > //etc...
    > > };

    >
    > > //etc...

    >
    > > };///struct memory_pig

    >
    > > memory_pig foo(){
    > > memory_pig result;
    > > result=something;
    > > //etc...
    > > result=something_else;
    > > return result;

    >
    > > };

    >
    > > any time 'foo' is called the output will contain the following
    > > sequence:

    >
    > > mem pig default
    > > mem pig copy
    > > mem pig finish

    >
    > > the last line of output may repeat based on how the result is
    > > stored(rvo) or not.
    > > So,two objects of a large type will be constructed and at least one is
    > > destructed on every call to 'foo'

    >
    > The C++ Standard already allows the "(Named) Return Value
    > Optimization" (NRVO or RVO for short). The optimization allows (under
    > certain conditions) the compiler to construct the result of a function
    > call "in place" - that is, directly in the object initialized with the
    > function call result.
    >
    > So, to take advantage of this optimization, a program should use the
    > result of a function call to initialize an object, instead of
    > assigning the function result to an existing object.
    >
    > For example:
    >
    > #include <iostream>
    >
    > using std::cout;
    >
    > struct memory_pig
    > { // a really large type:
    > memory_pig()
    > {
    > cout << "mem pig default\n";
    > }
    > memory_pig(memory_pig const&)
    > {
    > cout << "mem pig copy\n";
    > }
    > ~memory_pig()
    > {
    > cout << "mem pig finish\n";
    > }
    > };
    >
    > memory_pig foo()
    > {
    > memory_pig result;
    > // ...
    > return result;
    > }
    >
    > int main()
    > {
    > memory_pig m1 = foo();
    > memory_pig m2 = foo();
    > memory_pig m3 = foo();
    > }
    >
    > I compiled the above program twice, once with and once without NRVO.
    > The output of both programs is shown in the two columns below. As this
    > comparison shows, NRVO can be a particular effective optimization -
    > even for a small C++ program like the one used in this example.
    >
    > Program Output
    >
    > Without NRVO: With NRVO:
    >
    > mem pig default mem pig default
    > mem pig copy mem pig default
    > mem pig finish mem pig default
    > mem pig copy mem pig finish
    > mem pig finish mem pig finish
    > mem pig default mem pig finish
    > mem pig copy
    > mem pig finish
    > mem pig copy
    > mem pig finish
    > mem pig default
    > mem pig copy
    > mem pig finish
    > mem pig copy
    > mem pig finish
    > mem pig finish
    > mem pig finish
    > mem pig finish
    >

    I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    standard behavoir or unspecified?
    At least my compiler does not perform the NRVO.

    thanx,
    FM.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator, Oct 14, 2007
    #11
  12. On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    > On 2007-10-14 21:19, terminator wrote:
    >
    >
    >
    >
    >
    > > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    > >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:

    >
    > >> > consider:

    >
    > >> > struct memory_pig{//a really large type:

    >
    > >> > memory_pig(){
    > >> > std::cout<<"mem pig default\n";
    > >> > //etc...
    > >> > };

    >
    > >> > memory_pig(memory_pig const&){
    > >> > std::cout<<"mem pig copy\n";
    > >> > //etc...
    > >> > };

    >
    > >> > ~memory_pig(){
    > >> > std::cout<<"mem pig finish\n";
    > >> > //etc...
    > >> > };

    >
    > >> > //etc...

    >
    > >> > };///struct memory_pig

    >
    > >> > memory_pig foo(){
    > >> > memory_pig result;
    > >> > result=something;
    > >> > //etc...
    > >> > result=something_else;
    > >> > return result;

    >
    > >> > };

    >
    > >> > any time 'foo' is called the output will contain the following
    > >> > sequence:

    >
    > >> > mem pig default
    > >> > mem pig copy
    > >> > mem pig finish

    >
    > >> > the last line of output may repeat based on how the result is
    > >> > stored(rvo) or not.
    > >> > So,two objects of a large type will be constructed and at least one is
    > >> > destructed on every call to 'foo'

    >
    > >> The C++ Standard already allows the "(Named) Return Value
    > >> Optimization" (NRVO or RVO for short). The optimization allows (under
    > >> certain conditions) the compiler to construct the result of a function
    > >> call "in place" - that is, directly in the object initialized with the
    > >> function call result.

    >
    > >> So, to take advantage of this optimization, a program should use the
    > >> result of a function call to initialize an object, instead of
    > >> assigning the function result to an existing object.

    >
    > >> For example:

    >
    > >> #include <iostream>

    >
    > >> using std::cout;

    >
    > >> struct memory_pig
    > >> { // a really large type:
    > >> memory_pig()
    > >> {
    > >> cout << "mem pig default\n";
    > >> }
    > >> memory_pig(memory_pig const&)
    > >> {
    > >> cout << "mem pig copy\n";
    > >> }
    > >> ~memory_pig()
    > >> {
    > >> cout << "mem pig finish\n";
    > >> }
    > >> };

    >
    > >> memory_pig foo()
    > >> {
    > >> memory_pig result;
    > >> // ...
    > >> return result;
    > >> }

    >
    > >> int main()
    > >> {
    > >> memory_pig m1 = foo();
    > >> memory_pig m2 = foo();
    > >> memory_pig m3 = foo();
    > >> }

    >
    > >> I compiled the above program twice, once with and once without NRVO.
    > >> The output of both programs is shown in the two columns below. As this
    > >> comparison shows, NRVO can be a particular effective optimization -
    > >> even for a small C++ program like the one used in this example.

    >
    > > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    > > standard behavoir or unspecified?
    > > At least my compiler does not perform the NRVO.

    >
    > The standard allows this optimisation, but it does not require it (see
    > section 12.8 paragraph 15). I think that most newer compilers support
    > it, but you might have to turn up the optimisation level a bit.
    >
    > One important thing to remember is that when NRVO is in use the
    > destructor will not be called for the local object, nor will the copy-
    > constructor for the non-local object (m1 to m3 in the code above) be
    > called. Thus you should not depend on any side effects that the copy-
    > constructor or destructor might have.
    >


    prior to return and after return are two different case to me:I mean
    before return even move should be disabled ,while after the return
    either of move/copy must perform .

    regards,
    FM.


    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator(jam), Oct 15, 2007
    #12
  13. ===================================== MODERATOR'S COMMENT:

    Please trim unnecessary material when replying.


    ===================================== END OF MODERATOR'S COMMENT
    On 2007-10-15 17:52, terminator(jam) wrote:
    > On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    >> On 2007-10-14 21:19, terminator wrote:
    >>
    >>
    >>
    >>
    >>
    >> > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    >> >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:

    >>
    >> >> > consider:

    >>
    >> >> > struct memory_pig{//a really large type:

    >>
    >> >> > memory_pig(){
    >> >> > std::cout<<"mem pig default\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > memory_pig(memory_pig const&){
    >> >> > std::cout<<"mem pig copy\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > ~memory_pig(){
    >> >> > std::cout<<"mem pig finish\n";
    >> >> > //etc...
    >> >> > };

    >>
    >> >> > //etc...

    >>
    >> >> > };///struct memory_pig

    >>
    >> >> > memory_pig foo(){
    >> >> > memory_pig result;
    >> >> > result=something;
    >> >> > //etc...
    >> >> > result=something_else;
    >> >> > return result;

    >>
    >> >> > };

    >>
    >> >> > any time 'foo' is called the output will contain the following
    >> >> > sequence:

    >>
    >> >> > mem pig default
    >> >> > mem pig copy
    >> >> > mem pig finish

    >>
    >> >> > the last line of output may repeat based on how the result is
    >> >> > stored(rvo) or not.
    >> >> > So,two objects of a large type will be constructed and at least one is
    >> >> > destructed on every call to 'foo'

    >>
    >> >> The C++ Standard already allows the "(Named) Return Value
    >> >> Optimization" (NRVO or RVO for short). The optimization allows (under
    >> >> certain conditions) the compiler to construct the result of a function
    >> >> call "in place" - that is, directly in the object initialized with the
    >> >> function call result.

    >>
    >> >> So, to take advantage of this optimization, a program should use the
    >> >> result of a function call to initialize an object, instead of
    >> >> assigning the function result to an existing object.

    >>
    >> >> For example:

    >>
    >> >> #include <iostream>

    >>
    >> >> using std::cout;

    >>
    >> >> struct memory_pig
    >> >> { // a really large type:
    >> >> memory_pig()
    >> >> {
    >> >> cout << "mem pig default\n";
    >> >> }
    >> >> memory_pig(memory_pig const&)
    >> >> {
    >> >> cout << "mem pig copy\n";
    >> >> }
    >> >> ~memory_pig()
    >> >> {
    >> >> cout << "mem pig finish\n";
    >> >> }
    >> >> };

    >>
    >> >> memory_pig foo()
    >> >> {
    >> >> memory_pig result;
    >> >> // ...
    >> >> return result;
    >> >> }

    >>
    >> >> int main()
    >> >> {
    >> >> memory_pig m1 = foo();
    >> >> memory_pig m2 = foo();
    >> >> memory_pig m3 = foo();
    >> >> }

    >>
    >> >> I compiled the above program twice, once with and once without NRVO.
    >> >> The output of both programs is shown in the two columns below. As this
    >> >> comparison shows, NRVO can be a particular effective optimization -
    >> >> even for a small C++ program like the one used in this example.

    >>
    >> > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    >> > standard behavoir or unspecified?
    >> > At least my compiler does not perform the NRVO.

    >>
    >> The standard allows this optimisation, but it does not require it (see
    >> section 12.8 paragraph 15). I think that most newer compilers support
    >> it, but you might have to turn up the optimisation level a bit.
    >>
    >> One important thing to remember is that when NRVO is in use the
    >> destructor will not be called for the local object, nor will the copy-
    >> constructor for the non-local object (m1 to m3 in the code above) be
    >> called. Thus you should not depend on any side effects that the copy-
    >> constructor or destructor might have.
    >>

    >
    > prior to return and after return are two different case to me:I mean
    > before return even move should be disabled ,while after the return
    > either of move/copy must perform .


    Sorry, but you lost me there, what move? Of what to where?

    --
    Erik Wikström

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 15, 2007
    #13
  14. terminator(jam)

    Guest

    On Oct 9, 7:56 pm, "terminator(jam)" <> wrote:
    > consider:
    >
    > struct memory_pig{//a really large type:
    >
    > memory_pig(){
    > std::cout<<"mem pig default\n";
    > //etc...
    > };
    >
    > memory_pig(memory_pig const&){
    > std::cout<<"mem pig copy\n";
    > //etc...
    > };
    >
    > ~memory_pig(){
    > std::cout<<"mem pig finish\n";
    > //etc...
    > };
    >
    > //etc...
    >
    > };///struct memory_pig
    >
    > memory_pig foo(){
    > memory_pig result;
    > result=something;
    > //etc...
    > result=something_else;
    > return result;
    >
    > };
    >
    > any time 'foo' is called the output will contain the following
    > sequence:
    >
    > mem pig default
    > mem pig copy
    > mem pig finish


    It won't. It certainly won't do that with the example given, since
    there's no clear association in it between the copy constructor and
    the assignment operator (considering that the latter isn't even
    defined). Assuming there was such an association, though, I'd rather
    expect to see:

    mem pig default
    mem pig copy // first assignment
    mem pig copy // second assignment
    mem pig finish

    Which is correct and fine - if you have explicitly written the
    assignment operator twice, then surely you want the associated side-
    effects? And if you do not, then perhaps you shouldn't have written it
    that way.

    > that is you can refrence the returned object inside the function and
    > decrease the overhead for copying large objects.C++ lacks such syntax
    > and IMHO we should be able to mark the result object as referencing
    > the actual return so that there is no need for the extra copy
    > construction


    This is precisely what NRVO does.

    I have a feeling that you're confusing assignment with initialization
    in this case. Assignment operator always applies to an already-
    initialized object - the compiler cannot elide the initialization
    except for the trivial cases (e.g. POD types), which is why operator=
    still results in unnecessary copying even when the compiler implements
    NRVO. It's not a problem with NRVO though - it's because assignment is
    used where just initialization would suffice. Direct initialization is
    still more efficient then assignment-following-initialization, even
    with the move semantics of C++0x.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    , Oct 16, 2007
    #14
  15. On Oct 15, 8:35 pm, Erik Wikström <> wrote:
    > ===================================== MODERATOR'S COMMENT:
    >
    > Please trim unnecessary material when replying.
    >
    > ===================================== END OF MODERATOR'S COMMENT
    > On 2007-10-15 17:52, terminator(jam) wrote:
    >
    >
    >
    >
    >
    > > On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    > >> On 2007-10-14 21:19, terminator wrote:

    >
    > >> > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    > >> >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:

    >
    > >> >> > consider:

    >
    > >> >> > struct memory_pig{//a really large type:

    >
    > >> >> > memory_pig(){
    > >> >> > std::cout<<"mem pig default\n";
    > >> >> > //etc...
    > >> >> > };

    >
    > >> >> > memory_pig(memory_pig const&){
    > >> >> > std::cout<<"mem pig copy\n";
    > >> >> > //etc...
    > >> >> > };

    >
    > >> >> > ~memory_pig(){
    > >> >> > std::cout<<"mem pig finish\n";
    > >> >> > //etc...
    > >> >> > };

    >
    > >> >> > //etc...

    >
    > >> >> > };///struct memory_pig

    >
    > >> >> > memory_pig foo(){
    > >> >> > memory_pig result;
    > >> >> > result=something;
    > >> >> > //etc...
    > >> >> > result=something_else;
    > >> >> > return result;

    >
    > >> >> > };

    >
    > >> >> > any time 'foo' is called the output will contain the following
    > >> >> > sequence:

    >
    > >> >> > mem pig default
    > >> >> > mem pig copy
    > >> >> > mem pig finish

    >
    > >> >> > the last line of output may repeat based on how the result is
    > >> >> > stored(rvo) or not.
    > >> >> > So,two objects of a large type will be constructed and at least one is
    > >> >> > destructed on every call to 'foo'

    >
    > >> >> The C++ Standard already allows the "(Named) Return Value
    > >> >> Optimization" (NRVO or RVO for short). The optimization allows (under
    > >> >> certain conditions) the compiler to construct the result of a function
    > >> >> call "in place" - that is, directly in the object initialized with the
    > >> >> function call result.

    >
    > >> >> So, to take advantage of this optimization, a program should use the
    > >> >> result of a function call to initialize an object, instead of
    > >> >> assigning the function result to an existing object.

    >
    > >> >> For example:

    >
    > >> >> #include <iostream>

    >
    > >> >> using std::cout;

    >
    > >> >> struct memory_pig
    > >> >> { // a really large type:
    > >> >> memory_pig()
    > >> >> {
    > >> >> cout << "mem pig default\n";
    > >> >> }
    > >> >> memory_pig(memory_pig const&)
    > >> >> {
    > >> >> cout << "mem pig copy\n";
    > >> >> }
    > >> >> ~memory_pig()
    > >> >> {
    > >> >> cout << "mem pig finish\n";
    > >> >> }
    > >> >> };

    >
    > >> >> memory_pig foo()
    > >> >> {
    > >> >> memory_pig result;
    > >> >> // ...
    > >> >> return result;
    > >> >> }

    >
    > >> >> int main()
    > >> >> {
    > >> >> memory_pig m1 = foo();
    > >> >> memory_pig m2 = foo();
    > >> >> memory_pig m3 = foo();
    > >> >> }

    >
    > >> >> I compiled the above program twice, once with and once without NRVO.
    > >> >> The output of both programs is shown in the two columns below. As this
    > >> >> comparison shows, NRVO can be a particular effective optimization -
    > >> >> even for a small C++ program like the one used in this example.

    >
    > >> > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    > >> > standard behavoir or unspecified?
    > >> > At least my compiler does not perform the NRVO.

    >
    > >> The standard allows this optimisation, but it does not require it (see
    > >> section 12.8 paragraph 15). I think that most newer compilers support
    > >> it, but you might have to turn up the optimisation level a bit.

    >
    > >> One important thing to remember is that when NRVO is in use the
    > >> destructor will not be called for the local object, nor will the copy-
    > >> constructor for the non-local object (m1 to m3 in the code above) be
    > >> called. Thus you should not depend on any side effects that the copy-
    > >> constructor or destructor might have.

    >
    > > prior to return and after return are two different case to me:I mean
    > > before return even move should be disabled ,while after the return
    > > either of move/copy must perform .

    >
    > Sorry, but you lost me there, what move? Of what to where?
    >


    Move ctor of course.

    regards,
    FM.


    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator(jam), Oct 16, 2007
    #15
  16. On 2007-10-16 19:55, terminator(jam) wrote:
    > On Oct 15, 8:35 pm, Erik Wikström <> wrote:


    >> On 2007-10-15 17:52, terminator(jam) wrote:


    >> > On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    >> >> On 2007-10-14 21:19, terminator wrote:

    >>
    >> >> > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    >> >> >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:


    >> >> >> The C++ Standard already allows the "(Named) Return Value
    >> >> >> Optimization" (NRVO or RVO for short). The optimization allows (under
    >> >> >> certain conditions) the compiler to construct the result of a function
    >> >> >> call "in place" - that is, directly in the object initialized with the
    >> >> >> function call result.

    >>
    >> >> >> So, to take advantage of this optimization, a program should use the
    >> >> >> result of a function call to initialize an object, instead of
    >> >> >> assigning the function result to an existing object.

    >>
    >> >> >> For example:

    >>
    >> >> >> #include <iostream>

    >>
    >> >> >> using std::cout;

    >>
    >> >> >> struct memory_pig
    >> >> >> { // a really large type:
    >> >> >> memory_pig()
    >> >> >> {
    >> >> >> cout << "mem pig default\n";
    >> >> >> }
    >> >> >> memory_pig(memory_pig const&)
    >> >> >> {
    >> >> >> cout << "mem pig copy\n";
    >> >> >> }
    >> >> >> ~memory_pig()
    >> >> >> {
    >> >> >> cout << "mem pig finish\n";
    >> >> >> }
    >> >> >> };

    >>
    >> >> >> memory_pig foo()
    >> >> >> {
    >> >> >> memory_pig result;
    >> >> >> // ...
    >> >> >> return result;
    >> >> >> }

    >>
    >> >> >> int main()
    >> >> >> {
    >> >> >> memory_pig m1 = foo();
    >> >> >> memory_pig m2 = foo();
    >> >> >> memory_pig m3 = foo();
    >> >> >> }

    >>
    >> >> >> I compiled the above program twice, once with and once without NRVO.
    >> >> >> The output of both programs is shown in the two columns below. As this
    >> >> >> comparison shows, NRVO can be a particular effective optimization -
    >> >> >> even for a small C++ program like the one used in this example.

    >>
    >> >> > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    >> >> > standard behavoir or unspecified?
    >> >> > At least my compiler does not perform the NRVO.

    >>
    >> >> The standard allows this optimisation, but it does not require it (see
    >> >> section 12.8 paragraph 15). I think that most newer compilers support
    >> >> it, but you might have to turn up the optimisation level a bit.

    >>
    >> >> One important thing to remember is that when NRVO is in use the
    >> >> destructor will not be called for the local object, nor will the copy-
    >> >> constructor for the non-local object (m1 to m3 in the code above) be
    >> >> called. Thus you should not depend on any side effects that the copy-
    >> >> constructor or destructor might have.

    >>
    >> > prior to return and after return are two different case to me:I mean
    >> > before return even move should be disabled ,while after the return
    >> > either of move/copy must perform .

    >>
    >> Sorry, but you lost me there, what move? Of what to where?
    >>

    >
    > Move ctor of course.


    Sorry, but I still do not understand. What do you mean with RNVO should
    be disabled before return but not after? It is something that is
    performed *during* return.

    --
    Erik Wikström

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 16, 2007
    #16
  17. On Oct 16, 8:44 am, wrote:
    > On Oct 9, 7:56 pm, "terminator(jam)" <> wrote:
    >
    >
    >
    >
    >
    > > consider:

    >
    > > struct memory_pig{//a really large type:

    >
    > > memory_pig(){
    > > std::cout<<"mem pig default\n";
    > > //etc...
    > > };

    >
    > > memory_pig(memory_pig const&){
    > > std::cout<<"mem pig copy\n";
    > > //etc...
    > > };

    >
    > > ~memory_pig(){
    > > std::cout<<"mem pig finish\n";
    > > //etc...
    > > };

    >
    > > //etc...

    >
    > > };///struct memory_pig

    >
    > > memory_pig foo(){
    > > memory_pig result;
    > > result=something;
    > > //etc...
    > > result=something_else;
    > > return result;

    >
    > > };

    >
    > > any time 'foo' is called the output will contain the following
    > > sequence:

    >
    > > mem pig default
    > > mem pig copy
    > > mem pig finish

    >
    > It won't. It certainly won't do that with the example given, since
    > there's no clear association in it between the copy constructor and
    > the assignment operator (considering that the latter isn't even
    > defined). Assuming there was such an association, though, I'd rather
    > expect to see:
    >
    > mem pig default
    > mem pig copy // first assignment
    > mem pig copy // second assignment
    > mem pig finish
    >

    As I wrote before its just an illustration and I did not mean any
    assignment,replace it with any function( eg. modify(result) ;).

    > Which is correct and fine - if you have explicitly written the
    > assignment operator twice, then surely you want the associated side-
    > effects? And if you do not, then perhaps you shouldn't have written it
    > that way.
    >
    > > that is you can refrence the returned object inside the function and
    > > decrease the overhead for copying large objects.C++ lacks such syntax
    > > and IMHO we should be able to mark the result object as referencing
    > > the actual return so that there is no need for the extra copy
    > > construction

    >
    > This is precisely what NRVO does.
    >


    I was certain about the NRVO by the time I started the post.However
    NRVO is a compiler option(ugly!!!) not a demand .With NRVO we have two
    distinct phases optimize simultaniously : prior to return inside the
    callee and after that inside the caller:

    A foo(){
    A ret;//this will be return under any conditions.
    //....
    return ret;//pre return :no copy/move
    };
    class A a=foo();//after return : copy/move to a


    > I have a feeling that you're confusing assignment with initialization
    > in this case. Assignment operator always applies to an already-
    > initialized object - the compiler cannot elide the initialization
    > except for the trivial cases (e.g. POD types), which is why operator=
    > still results in unnecessary copying even when the compiler implements
    > NRVO. It's not a problem with NRVO though - it's because assignment is
    > used where just initialization would suffice. Direct initialization is
    > still more efficient then assignment-following-initialization, even
    > with the move semantics of C++0x.


    Everybody makes mistakes while learning , but confusing assignment
    with copy certainly has never been the mistake I can make.

    regards,
    FM

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator(jam), Oct 16, 2007
    #17
  18. On Oct 16, 9:37 pm, (Erik Wikström) wrote:
    > On 2007-10-16 19:55, terminator(jam) wrote:
    >
    >
    >
    >
    >
    > > On Oct 15, 8:35 pm, Erik Wikström <> wrote:
    > >> On 2007-10-15 17:52, terminator(jam) wrote:
    > >> > On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    > >> >> On 2007-10-14 21:19, terminator wrote:

    >
    > >> >> > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    > >> >> >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:
    > >> >> >> The C++ Standard already allows the "(Named) Return Value
    > >> >> >> Optimization" (NRVO or RVO for short). The optimization allows (under
    > >> >> >> certain conditions) the compiler to construct the result of a function
    > >> >> >> call "in place" - that is, directly in the object initialized with the
    > >> >> >> function call result.

    >
    > >> >> >> So, to take advantage of this optimization, a program should use the
    > >> >> >> result of a function call to initialize an object, instead of
    > >> >> >> assigning the function result to an existing object.

    >
    > >> >> >> For example:

    >
    > >> >> >> #include <iostream>

    >
    > >> >> >> using std::cout;

    >
    > >> >> >> struct memory_pig
    > >> >> >> { // a really large type:
    > >> >> >> memory_pig()
    > >> >> >> {
    > >> >> >> cout << "mem pig default\n";
    > >> >> >> }
    > >> >> >> memory_pig(memory_pig const&)
    > >> >> >> {
    > >> >> >> cout << "mem pig copy\n";
    > >> >> >> }
    > >> >> >> ~memory_pig()
    > >> >> >> {
    > >> >> >> cout << "mem pig finish\n";
    > >> >> >> }
    > >> >> >> };

    >
    > >> >> >> memory_pig foo()
    > >> >> >> {
    > >> >> >> memory_pig result;
    > >> >> >> // ...
    > >> >> >> return result;
    > >> >> >> }

    >
    > >> >> >> int main()
    > >> >> >> {
    > >> >> >> memory_pig m1 = foo();
    > >> >> >> memory_pig m2 = foo();
    > >> >> >> memory_pig m3 = foo();
    > >> >> >> }

    >
    > >> >> >> I compiled the above program twice, once with and once without NRVO.
    > >> >> >> The output of both programs is shown in the two columns below. As this
    > >> >> >> comparison shows, NRVO can be a particular effective optimization -
    > >> >> >> even for a small C++ program like the one used in this example.

    >
    > >> >> > I knew about RVO but not NRVO(that is what I am talking about).Is NRVO
    > >> >> > standard behavoir or unspecified?
    > >> >> > At least my compiler does not perform the NRVO.

    >
    > >> >> The standard allows this optimisation, but it does not require it (see
    > >> >> section 12.8 paragraph 15). I think that most newer compilers support
    > >> >> it, but you might have to turn up the optimisation level a bit.

    >
    > >> >> One important thing to remember is that when NRVO is in use the
    > >> >> destructor will not be called for the local object, nor will the copy-
    > >> >> constructor for the non-local object (m1 to m3 in the code above) be
    > >> >> called. Thus you should not depend on any side effects that the copy-
    > >> >> constructor or destructor might have.

    >
    > >> > prior to return and after return are two different case to me:I mean
    > >> > before return even move should be disabled ,while after the return
    > >> > either of move/copy must perform .

    >
    > >> Sorry, but you lost me there, what move? Of what to where?

    >
    > > Move ctor of course.

    >
    > Sorry, but I still do not understand. What do you mean with RNVO should
    > be disabled before return but not after? It is something that is
    > performed *during* return.


    I mean the whole concept of optimization(rvo,nrvo,etc). IMHO:
    If a function always returns the same local object the returned object
    must be the local object itself rather than a copy/move version of the
    local.
    If a function returns one of its parameter values returned object is
    copy/moved from that parameter because a change in location of the
    object is almost inevitable.
    If an object is constructed with an rvalue it should be copy/moved.

    regards,
    FM



    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    terminator(jam), Oct 17, 2007
    #18
  19. On 2007-10-17 17:53, terminator(jam) wrote:
    > On Oct 16, 9:37 pm, (Erik Wikström) wrote:
    >> On 2007-10-16 19:55, terminator(jam) wrote:
    >> > On Oct 15, 8:35 pm, Erik Wikström <> wrote:
    >> >> On 2007-10-15 17:52, terminator(jam) wrote:
    >> >> > On Oct 14, 10:00 pm, (Erik Wikström) wrote:
    >> >> >> On 2007-10-14 21:19, terminator wrote:

    >>
    >> >> >> > On Oct 14, 11:08 am, Greg Herlihy <> wrote:
    >> >> >> >> On Oct 9, 8:56 am, "terminator(jam)" <> wrote:
    >> >> >> >> The C++ Standard already allows the "(Named) Return Value
    >> >> >> >> Optimization" (NRVO or RVO for short). The optimization allows (under
    >> >> >> >> certain conditions) the compiler to construct the result of a function
    >> >> >> >> call "in place" - that is, directly in the object initialized with the
    >> >> >> >> function call result.

    >>
    >> >> >> >> So, to take advantage of this optimization, a program should use the
    >> >> >> >> result of a function call to initialize an object, instead of
    >> >> >> >> assigning the function result to an existing object.

    >>
    >> >> >> >> For example:

    >>
    >> >> >> >> #include <iostream>

    >>
    >> >> >> >> using std::cout;

    >>
    >> >> >> >> struct memory_pig
    >> >> >> >> { // a really large type:
    >> >> >> >> memory_pig()
    >> >> >> >> {
    >> >> >> >> cout << "mem pig default\n";
    >> >> >> >> }
    >> >> >> >> memory_pig(memory_pig const&)
    >> >> >> >> {
    >> >> >> >> cout << "mem pig copy\n";
    >> >> >> >> }
    >> >> >> >> ~memory_pig()
    >> >> >> >> {
    >> >> >> >> cout << "mem pig finish\n";
    >> >> >> >> }
    >> >> >> >> };

    >>
    >> >> >> >> memory_pig foo()
    >> >> >> >> {
    >> >> >> >> memory_pig result;
    >> >> >> >> // ...
    >> >> >> >> return result;
    >> >> >> >> }

    >>
    >> >> >> >> int main()
    >> >> >> >> {
    >> >> >> >> memory_pig m1 = foo();
    >> >> >> >> memory_pig m2 = foo();
    >> >> >> >> memory_pig m3 = foo();
    >> >> >> >> }

    >>
    >> >> >> >> I compiled the above program twice, once with and once without NRVO.
    >> >> >> >> The output of both programs is shown in the two columns below.. As this
    >> >> >> >> comparison shows, NRVO can be a particular effective optimization -
    >> >> >> >> even for a small C++ program like the one used in this example.

    >>
    >> >> >> > I knew about RVO but not NRVO(that is what I am talking about)..Is NRVO
    >> >> >> > standard behavoir or unspecified?
    >> >> >> > At least my compiler does not perform the NRVO.

    >>
    >> >> >> The standard allows this optimisation, but it does not require it (see
    >> >> >> section 12.8 paragraph 15). I think that most newer compilers support
    >> >> >> it, but you might have to turn up the optimisation level a bit.

    >>
    >> >> >> One important thing to remember is that when NRVO is in use the
    >> >> >> destructor will not be called for the local object, nor will the copy-
    >> >> >> constructor for the non-local object (m1 to m3 in the code above) be
    >> >> >> called. Thus you should not depend on any side effects that the copy-
    >> >> >> constructor or destructor might have.

    >>
    >> >> > prior to return and after return are two different case to me:I mean
    >> >> > before return even move should be disabled ,while after the return
    >> >> > either of move/copy must perform .

    >>
    >> >> Sorry, but you lost me there, what move? Of what to where?

    >>
    >> > Move ctor of course.

    >>
    >> Sorry, but I still do not understand. What do you mean with RNVO should
    >> be disabled before return but not after? It is something that is
    >> performed *during* return.

    >
    > I mean the whole concept of optimization(rvo,nrvo,etc). IMHO:
    > If a function always returns the same local object the returned object
    > must be the local object itself


    You mean for a function such as

    foo bar()
    {
    foo f;
    return f;
    }

    NRVO should be used? That is (to my knowledge) the case if NRVO is
    enabled in the compiler.

    > rather than a copy/move version of the local.


    What is the difference between copy and move? I would say that NRVO is
    move, since the object created in the function is moved to the
    "surrounding" context/scope instead of being used as the basis of a copy.

    > If a function returns one of its parameter values returned object is
    > copy/moved from that parameter because a change in location of the
    > object is almost inevitable.


    Assuming that you by copy/move mean that the object in the function is
    being copied to the surrounding scope, did you mean that something like

    foo bar(bool b)
    {
    foo f1, f2;
    if (b)
    return f1;
    else
    return f2;
    }

    should not use NRVO? Again, to my knowledge that is the case, since the
    compiler can not predict which of the objects will be returned so a copy
    is necessary.

    > If an object is constructed with an rvalue it should be copy/moved.


    You mean something similar to

    foo bar()
    {
    foo f1, f2;
    return f1 + f2;
    }

    In that case NRVO could be used depending on whether the compiler knows
    which object will be returned or not. So the above could utilise NRVO
    but something like

    foo bar(bool b)
    {
    foo f1, f2, f3;
    if (b)
    return f1 + f2;
    else
    return f2 + f3;
    }

    could not.

    If the above is not what you meant, then I am sorry to have failed to
    understand you. Perhaps you could try to rephrase the question or use
    simple code examples.

    --
    Erik Wikström

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    =?UTF-8?B?RXJpayBXaWtzdHLDtm0=?=, Oct 17, 2007
    #19
  20. terminator(jam)

    Guest

    On 16 Oct, 23:26, "terminator(jam)" <> wrote:
    > A foo(){
    > A ret;//this will be return under any conditions.
    > //....
    > return ret;//pre return :no copy/move};
    >
    > class A a=foo();//after return : copy/move to a


    This is not the case. If the compiler correctly implements NRVO, the
    example above will result in a single construction of A using the
    default constructor, and then a subsequent destruction. No copies or
    moves are made at any point.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
    , Oct 17, 2007
    #20
    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. Rocky Moore
    Replies:
    7
    Views:
    1,712
    mikeb
    Jan 14, 2004
  2. =?Utf-8?B?S2V2aW4gQnVydG9u?=

    Pre-Send Request Headers, Pre-Send Request Content

    =?Utf-8?B?S2V2aW4gQnVydG9u?=, Dec 31, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    1,026
    =?Utf-8?B?S2V2aW4gQnVydG9u?=
    Dec 31, 2004
  3. Wladimir Borsov
    Replies:
    7
    Views:
    461
    Raymond Loman
    May 5, 2004
  4. Replies:
    2
    Views:
    401
  5. Ravikiran

    Zero Optimization and Sign Optimization???

    Ravikiran, Nov 17, 2008, in forum: C Programming
    Replies:
    22
    Views:
    836
    Thad Smith
    Nov 24, 2008
Loading...

Share This Page