Thread: why visual studio does not optimize constructor in this case

  1. #1
    Registered User
    Join Date
    May 2006
    Posts
    1,579

    why visual studio does not optimize constructor in this case

    Hello everyone,


    Why visual studio does not optimize constructor in this case? I do not understand what the MSDN mentioned,

    if use different named object, compiler can not optimize. Why?

    http://msdn2.microsoft.com/en-us/lib...57(vs.80).aspx

    Code:
    #include <stdio.h>
    class RVO
    {
    public:
           
                RVO(){printf("I am in constructor\n");}
                RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
                int mem_var;       
    };
    RVO MyMethod (int i)
    {
                RVO rvo;
                rvo.mem_var = i;
          if (rvo.mem_var == 10)
             return (RVO());
                return (rvo); 
    }
    int main()
    {
                RVO rvo;
                rvo=MyMethod(5);
    }
    Output is,

    I am in constructor
    I am in constructor
    I am in copy constructor

    My expected output is,

    I am in constructor
    I am in constructor


    thanks in advance,
    George

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    If we look at it from a language lawyer point of view, the C++ Standard states that "in such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object". So, with different paths returning different named objects, which object to be returned should the implementation treat as the object from the caller? Since this cannot be determined at compile time, the return value optimisation cannot take place.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks laserlight,


    I do not know why diferent execution path will disable compiler from optimization. In the sample, there are two execution paths to return,

    1. return (RVO());

    In case (1), compiler could optimize by saving the creating of the temporary object and assign to the extern rvo object directly.

    2. return (rvo);

    In this case (2), compiler could optimize by not creating the temporary object for the return value, and assign the inner rvo to outter rvo directly.

    Why compiler can not optimize it?


    regards,
    George


    Quote Originally Posted by laserlight View Post
    If we look at it from a language lawyer point of view, the C++ Standard states that "in such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object". So, with different paths returning different named objects, which object to be returned should the implementation treat as the object from the caller? Since this cannot be determined at compile time, the return value optimisation cannot take place.

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    If the return paths behave differently, then the compiler cannot know what code to generate at compile time and thus cannot optimize it at all. The compiler doesn't know which return path will be taken and cannot guess, so it cannot generate code because most likely it will generate faulty code.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Elysia,


    Why compiler can not optimize code for all the return paths? As I mentioned, in either return path, we can optimize the code by saving the time to create temporary object.

    Anything wrong in my analysis?

    Quote Originally Posted by Elysia View Post
    If the return paths behave differently, then the compiler cannot know what code to generate at compile time and thus cannot optimize it at all. The compiler doesn't know which return path will be taken and cannot guess, so it cannot generate code because most likely it will generate faulty code.

    regards,
    George

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I don't believe that's the problem. The compiler has to make sure the generated code works, and it cannot analyze for all situations when there are multiple returns paths or the compiler doesn't support it. I'm thinking it's the code when assigning the object to the temporary variable that cannot be optimized due to the different return paths, and thus the compiler cannot optimize anything.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    RVO and NRVO work by constructing the object that is to be returned directly in the space provided by the caller, instead of constructing the object in the local variables of the function and then copying it over to the caller space.

    Let's look at the sample.
    Code:
    RVO MyMethod (int i)
    {
        RVO rvo;
        rvo.mem_var = i;
        if (rvo.mem_var == 10)
            return (RVO());
        return (rvo); 
    }
    The issue is this: the rvo object must be constructed before the assignment to mem_var. Where should the compiler construct the object? If NRVO takes effect, the object is constructed directly in the space provided by the caller.
    However, what if the alternate path is taken? Then the object that actually should be constructed in this space is not rvo, but the unnamed temporary that should be returned. If NRVO had been applied, this space would already be taken by rvo, leading to a conflict.
    That's why rvo has to be a separate variable in the function's own space. NRVO cannot be applied. RVO for the unnamed temporary can still be applied.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    This is why at some point in every programmers career they should try writing a compiler. It help you appreciate how difficult these things are. It teaches you about what is easy or difficult to optimise.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  9. #9
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Elysia,


    But I do not know why which named variable/return path matters the optimization -- the optimization only happens when the function returns and compiler only needs to insert optimization code at the place where there is a return statement -- if there are multiple returns, the compiler could put multiple optimization into multiple return statement.

    I think no matter which one will return, either rvo or RVO(), the return value is a temporary obejct, and compiler could optimize it by using assignment operator on the outside rvo object instance directly without creating the temporary object, right?

    Why the named variable/return path matters? Could you provide more information about your analysis please?

    Quote Originally Posted by Elysia View Post
    I don't believe that's the problem. The compiler has to make sure the generated code works, and it cannot analyze for all situations when there are multiple returns paths or the compiler doesn't support it. I'm thinking it's the code when assigning the object to the temporary variable that cannot be optimized due to the different return paths, and thus the compiler cannot optimize anything.

    regards,
    George

  10. #10
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi iMalc,


    Any more specific analysis for the issue why compiler does not optimize the code in this case? :-)

    Quote Originally Posted by iMalc View Post
    This is why at some point in every programmers career they should try writing a compiler. It help you appreciate how difficult these things are. It teaches you about what is easy or difficult to optimise.

    regards,
    George

  11. #11
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi CornedBee,


    Thanks for sharing your perspective. But I do not agree with you. :-)

    I think either rvo or RVO() is returned, compiler could optimize by not generating code to put a temporary object on the return stack by creating the copy constructor of the temporary object, and optimize it by invoking the outter rvo (in main)'s assignment operator directly.

    This is the point why I am confused. I am not sure why compiler can not optimize in a similar way as I mentioned above.

    I have also performed some further testing, if we change return RVO() to another named rvo instance, but different return path using different named rvo instance, the code can not be optimized either.

    Code:
    class RVO 
    { 
    public: 
    
    RVO(){printf("I am in constructor\n");} 
    RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");} 
    int mem_var; 
    }; 
    RVO MyMethod (int i) 
    { 
    RVO rvo1;
    RVO rvo2;
    rvo1.mem_var = i; 
    rvo2.mem_var = i; 
    if (rvo1.mem_var == 10) 
    return (rvo1); 
    // return (RVO()); 
    return (rvo2); 
    } 
    int main() 
    { 
    	RVO rvo;
    	rvo = MyMethod(5);
    
    	return 0;
    }
    Output is,

    --------------------
    I am in constructor
    I am in constructor
    I am in constructor
    I am in copy constructor
    --------------------

    Quote Originally Posted by CornedBee View Post
    RVO and NRVO work by constructing the object that is to be returned directly in the space provided by the caller, instead of constructing the object in the local variables of the function and then copying it over to the caller space.

    Let's look at the sample.
    Code:
    RVO MyMethod (int i)
    {
        RVO rvo;
        rvo.mem_var = i;
        if (rvo.mem_var == 10)
            return (RVO());
        return (rvo); 
    }
    The issue is this: the rvo object must be constructed before the assignment to mem_var. Where should the compiler construct the object? If NRVO takes effect, the object is constructed directly in the space provided by the caller.
    However, what if the alternate path is taken? Then the object that actually should be constructed in this space is not rvo, but the unnamed temporary that should be returned. If NRVO had been applied, this space would already be taken by rvo, leading to a conflict.
    That's why rvo has to be a separate variable in the function's own space. NRVO cannot be applied. RVO for the unnamed temporary can still be applied.

    regards,
    George

  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by George2 View Post
    I think either rvo or RVO() is returned, compiler could optimize by not generating code to put a temporary object on the return stack by creating the copy constructor of the temporary object, and optimize it by invoking the outer rvo (in main)'s assignment operator directly.
    That would mean optimizing the function body specifically for the single call. It would mean that, for another call to the function, a different body would have to be generated. It would mean breaking the function calling convention.

    The problem is that this is no longer covered by the RVO clause, and thus the compiler is no longer allowed to elide the copying without proving that it is side-effect-free. (And in the example, the copy constructor isn't side-effect-free.) There are various other problems with it, all resulting in making this particular case of optimization very hard to prove right and very complex to implement. In other words, not worth the effort.

    The compiler might do it if it were to inline the call completely, but the function seems a little too complex for that.

    I have also performed some further testing, if we change return RVO() to another named rvo instance, but different return path using different named rvo instance, the code can not be optimized either.
    Exactly the same situation. The moment there is more than one potential object to return, it won't work anymore.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. more then 100errors in header
    By hallo007 in forum Windows Programming
    Replies: 20
    Last Post: 05-13-2007, 08:26 AM
  2. load gif into program
    By willc0de4food in forum Windows Programming
    Replies: 14
    Last Post: 01-11-2006, 10:43 AM
  3. pointer to array of objects of struct
    By undisputed007 in forum C++ Programming
    Replies: 12
    Last Post: 03-02-2004, 04:49 AM
  4. Linking error
    By DockyD in forum C++ Programming
    Replies: 10
    Last Post: 01-20-2003, 05:27 AM
  5. A simple array question
    By frenchfry164 in forum C++ Programming
    Replies: 7
    Last Post: 11-25-2001, 04:13 PM