Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Second guessing the compiler?



  • I have code like this:

    void function(QPointF p){
        const float x = p.x();
        const float y = p.y();
        const float w = p.width();
        const float h = p.height();    
    
        // use vars for whatever
    }
    

    Am I second guessing the compiler here? Will the compiler optimize the function call out after the first call if I use p.x() in more than one place?


  • Lifetime Qt Champion

    @fcarney said in Second guessing the compiler?:

    Will the compiler optimize the function call out after the first call if I use p.x() in more than one place?

    You mean when you call function() with the same point again?



  • @Christian-Ehrlicher said in Second guessing the compiler?:

    You mean when you call function() with the same point again?

    Yes, I am wondering if the other calls get optimized out as long as the object does not change.



  • @fcarney
    One thing I think will certainly be true: it will depend on compiler and code optimization flags. Are you expecting the same behaviour from msvc -O2 and gcc -g?

    You will not know for sure what it might or might not do. If you don't want p.x() to be evaluated again, push its result to a variable.

    And if you really mean will code be generated which recognises whether the incoming parameter's value has changed across calls to the function, I think you can assume that is unlikely. The optimizations in the compiled code will be over multiple calls to p.x() within a function, where it can see p has not been changed by logic. And furthermore, because p.x() is a function call, not just a reference, it will only optimize calls out at all if it knows from the definition that there are no side-effects/extra code, and it's a clever optimizer :)

    What does the declaration of QPointF::x() look like anyway, is it an inline?


  • Lifetime Qt Champion

    @fcarney said in Second guessing the compiler?:

    I am wondering if the other calls get optimized out as long as the object does not change.

    Since you did not post the full code - no
    Your current code will be completely removed by the optimizer since it does nothing.


  • Moderators

    @fcarney said in Second guessing the compiler?:

    Yes, I am wondering if the other calls get optimized out as long as the object does not change.

    No, not as a rule. But compiler optimizers are pretty complex, so my advice is - write it as it is most obvious/convenient for you and leave the compiler to do its magic; a.k.a. have love and faith in your friendly compiler writer, not doubt. ;)

    @JonB said in Second guessing the compiler?:

    If you don't want p.x() to be evaluated again, push its result to a variable.

    Wrong. Putting something into a variable means nothing to the compiler. It can reorder and reorganize the code as it likes as long as it can prove the result is correct.

    What does the declaration of QPointF::x() look like anyway, is it an inline?

    Yes, it's inline.



  • @kshegunov said in Second guessing the compiler?:

    @JonB said in Second guessing the compiler?:

    If you don't want p.x() to be evaluated again, push its result to a variable.

    Wrong. Putting something into a variable means nothing to the compiler. It can reorder and reorganize the code as it likes as long as it can prove the result is correct.

    Not fair! Mine was intended as practical advice. What you can be sure of, from a validated compiler, is that it will not re-call a function which does/might/could have side-effects if you assign to a variable. Whereas you cannot say the same for sure of second-guessing whether the compiler might or might not save a call if you do not store to a variable and re-use the function call.

    Yes, it's inline.

    Then if it's nice and small like just returning a member, the whole discussion for the OP of whether to bother storing into variables is moot.... (Again, practical rather than theoretical observation.)


  • Moderators

    @JonB said in Second guessing the compiler?:

    What you can be sure of, from a validated compiler, is that it will not re-call a function which does/might/could have side-effects if you assign to variable.

    Fair argument, I agree.

    Then if it's nice and small like just returning a member

    Yes, it is. ... and I know you know we both know where this is all going to end ... ;D



  • One thing I am considering is whether or not the call could be to a shared object (threading). Is this case no, but as things get more and more parallel it may cause a change in coding style to change to be more defensive in this regard. Like making sure to take a snapshot of a whole object to prevent desynchronization of values (like x and y are related mathmatically). In that case QPointF temp = p; would protect against that.

    I didn't want to be creating boilerplate for no good reason. I do like the syntax better for just working with a local x variable. I don't like the extra object.func() syntax in a calculation. The const qualifier is defensive in this case as well. To prevent me from changing something because of autocomplete. Which has happened recently.

    Edit:
    Yes I realize I am working with a copy passed to the function. I am thinking of when using a reference or pointer that could be passed in for the potential desynchronization issues.


  • Moderators

    @fcarney said in Second guessing the compiler?:

    Like making sure to take a snapshot of a whole object to prevent desynchronization of values (like x and y are related mathmatically). In that case QPointF temp = p; would protect against that.

    This is what you'd usually do - use reentrant objects, i.e. copy instead of sharing the data. The same you do for ints, floats and whatever. You'd want to share the data only if:

    1. It's too big and the malice to performance from copying it is unacceptable. Delaying the copying is fine however (see. QSharedData and QSharedDataPointer)
    2. It's a global state which can't be avoided, then you have 2 options:
      • serialize the access (think mutexes and semaphores)
      • impose some restrictions on the user code so that you ensure you initialize and use the object(s) in a specific manner (e.g. QApplication)

    I didn't want to be creating boilerplate for no good reason. I do like the syntax better for just working with a local x variable. I don't like the extra object.func() syntax in a calculation. The const qualifier is defensive in this case as well. To prevent me from changing something because of autocomplete. Which has happened recently.

    That's all in line with what I said - do your code as good as you imagine it and trust the compiler do the right thing with that.

    Yes I realize I am working with a copy passed to the function. I am thinking of when using a reference or pointer that could be passed in for the potential desynchronization issues.

    If you're working with references then you almost universally need to make sure two things happen:

    1. You can handle the pointer itself safely, if that's needed (e.g. passing a pointer from one thread to the next). This is actually the main (and only) purpose of the shared pointer idiom (QSharedPointer).
    2. You serialize access to the data fields when done from different threads (mutexes and semaphores again).
      Important note: This is about the object, not the pointer! So it's completely independent from 1!

  • Moderators

    Useful reading on the topic of sharing data (including between threads): https://doc.qt.io/qt-5/implicit-sharing.html


Log in to reply