Thread: An NRV of a day

  1. #1
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446

    An NRV of a day

    Spent most of the day trying to track a bug on my code that would only manifest in release mode with full optimizations. After going nuts with it, I finally came across an obscure and old copy constructor that wasn't being called by the new code due to a NVR optimization.

    I know of RVO and NRVO for some time. But only today I came across its most obscure consequences. The question is what was the reasoning behind automatic NRV optimization when this clearly affects code flow? (RVO is not such an issue since under any case I can think of the elision of the copy constructor is obvious. But instead, NRVO is a tough nut to crack).

    Seems to me rather odd that an optimization with such an impact in our code (it breaks the ANSI C 'As If' rule) be treated with such leniency(?). Visual Studio is particularly pernicious since apparently it doesn't allow me to control this particular optimization in separate. GCC does provide -fno-elide-constructors, but it's still too little.

    I cannot even see if the optimization was applied at source level. I simply am not given any clue as to what is happening behind the stage. A language extension would be rather welcomed here. I don't mean syntactic sugar, but an implementation extension that allowed for a standardized way for compilers to show the optimization at source-level (or flag it at compile time). Why hasn't the Committee ever considered it?
    Last edited by Mario F.; 09-09-2009 at 06:27 PM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    In my opinion, having copy constructors that have side effects is extremely poor style to begin with, so the issue should be moot. It takes very careful thought, or very disciplined coding, to reliably identify all places an object could be copied.

    I'm not sure what language extension you're looking for here. The language standard only concerns itself with what a given piece of code should do. It has never given any directives about anything beyond that. There's only defined, undefined, ill-formed, and diagnostics.
    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

  3. #3
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    I don't think it is moot. The case being I don't have control over the library, so there isn't much I can do about it other than finding potential damaging copy constructors and code my way around the NRVO when handling instances of those classes.

    My first attempt was indeed to find a compiler switch to turn off NRVO. Unfortunately there is none under VC++ which forces me to remove other important optimizations (out of the question). I also looked into the pre-compiled code to see if I could spot evidence of the optimization being applied, there is no such thing. And this is what troubled me the most, since here I have a clear potential for an 'As If' rule being to be broken without any way of knowing about it. More important now because here I am in the presence of old code that behaves differently on the new standard.

    I'm not sure what I mean by language extension. I can't think of a clear example other than the compiler adding the return type to the parameters list:

    Code:
    foo func(int val) {
    
        foo bar;
    
        // do something
    
        return bar;
    }
    
    //would be changed by the compiler to:
    
    void func(foo& temp, int val) {
    
        // use temp
    
    }
    But this probably may not be done in all cases? Or supposedly reduces the optimization.

    So while I can't find a clear idea to what could be a possible language extension, I find disconcerting I cannot verify if this optimization is being applied when it clearly alters program flow.

    EDIT: In case you are wondering, the library is an in-house implementation for the software that maintains their factory line and implements a logging mechanism that includes the copy constructors of many classes. Regardless of any coding style considerations, this wasn't much a problem to the makers of this library some years back, because from my parallel readings on this issue only recently (2005) VC++ implemented NRVO. Will never know if they were just ignorant of NRVO and it eventually being implemented, or where just being lazy. But the reality is that the problem is now mine.
    Last edited by Mario F.; 09-10-2009 at 11:02 AM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You could - at a high cost to code stability - probably trick MSVC into not applying the NRVO. It is documented rather thoroughly, including its limitations. You could try to create a situation that doesn't change code behavior but makes it impossible for the optimizer to omit the copy.
    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

  5. #5
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    There's one possible non costing solution. Maybe you could help...

    If I reference the return object inside an asm block, the NRVO will not kick in. May aim would be to find an inconsequential instruction I could use.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  6. #6
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by Mario F. View Post
    There's one possible non costing solution. Maybe you could help...

    If I reference the return object inside an asm block, the NRVO will not kick in. May aim would be to find an inconsequential instruction I could use.
    NOP?

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Nah. I need it to be referenced inside the block. And ideally I need something that doesn't offer a great deal of attrition.

    Code:
    foo func()
    {
        foo bar;
        __asm {
            mov eax, bar
            mov bar, eax
        }
    
        return bar; 
    }
    This doesn't trigger NRVO. bar is being referenced inside the asm block. I need to find a less costly approach.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Ah, right - I missed that part. Well, I can't imagine anything much cheaper than two mov instructions. What about a 'dummy' function?

    Code:
    void suppress_nrvo( void* );

  9. #9
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Quote Originally Posted by Sebastiani View Post
    What about a 'dummy' function?

    Code:
    void suppress_nrvo( void* );
    I'm not following...
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  10. #10
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    As in:

    Code:
    foo func()
    {
        foo bar;
        suppress_nrvo(&bar);
        return bar; 
    }
    I'm not totally sure that would work, but it may well.

  11. #11
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Oooh... and it looks sexy! Wouldn't mind paying the function call cost for the expressiveness you just offered me.

    Will try right now. Be back in a bit.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    And of course, even if that does work then the compiler is probably going put some default instructions in the function, making it perhaps "more expensive" than the two mov instructions (although you could compile the function as a separate object file, and then edit the first byte of the function code to be a ret instruction...).

  13. #13
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    Calling a function (push parameter, push return address, jump, push ebp, mov ebp esp, pop ebp, ret, add esp, 4) is less expensive than 2 mov's?

  14. #14
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Code:
    #include <iostream>
    
    class foo {
    public:
    	foo() {}
    	foo(const foo&) { std::cout << "RVO was not applied!\n"; }
    };
    
    #pragma warning (suppress : 4100)
    void suppress_nrvo(void* v) { }
    
    foo NOT_NRVO() { foo bar; suppress_nrvo(&bar); return bar; }
    foo NRVO() { foo bar; return bar; }
    
    int main() {
    
    	std::cout << "Fire NRVO\n";
    	foo one = NRVO();
    	std::cout << "Fire NOT_NRVO\n";
    	foo two = NOT_NRVO();
    
    }
    It didn't work. Output was:

    Fire NRVO
    Fire NOT_NRVO
    In any case I liked this solution so much I peed my pants. It's 5am over here and really need to go to bed. I'm suppose to go off to work in 5 hours. Will play with suppress_nrvo() definition to force the compiler to not optimize it. that's probably why it didn't work. The function was eliminated.

    Thanks a million Sebastiani.
    Calling a function (push parameter, push return address, jump, push ebp, mov ebp esp, pop ebp, ret, add esp, 4) is less expensive than 2 mov's?
    It's not, cyberfish. But he offered me a very expressive solution. One I was struggling also to get. This gives me a more elegant approach and one that can be easily changed in the future once I convince them to let me code a new library. If the tests allow for the performance loss (which I can only hope they will) this is the best of both worlds.
    Last edited by Mario F.; 09-10-2009 at 10:12 PM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  15. #15
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    Same result under GCC 4.3.3, with no optimization (-O0).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Should I learn a FULL language first?
    By Raeliean in forum Game Programming
    Replies: 8
    Last Post: 07-16-2005, 06:59 PM
  2. error with code
    By duffy in forum C Programming
    Replies: 8
    Last Post: 10-22-2002, 09:45 PM
  3. cant get code to work
    By duffy in forum C Programming
    Replies: 13
    Last Post: 10-20-2002, 05:23 AM
  4. debug program
    By new_c in forum C Programming
    Replies: 3
    Last Post: 03-18-2002, 11:50 PM
  5. Simplified code
    By soonerfan in forum C Programming
    Replies: 2
    Last Post: 12-05-2001, 03:50 PM