Understanding the cost of multiple. and & # 8594; using the operator?

Out of habit, when accessing values ​​through. or -> I assign them to variables anytime the value will be used more than once. I understand that in scripting languages ​​like actionscript this is very important. However, in C / C ++, I am wondering if this is a pointless task; Am I wasting the effort the compiler will handle for me, or am I using good practice and why?

 public struct Foo
    {
        public:
        Foo(int val){m_intVal = val;)
        int GetInt(){return m_intVal;}

        int m_intVal; // not private for sake of last example
    };
    public void Bar()
    {
        Foo* foo = GetFooFromSomewhere();
        SomeFuncUsingIntValA(foo->GetInt()); // accessing via dereference then function
        SomeFuncUsingIntValB(foo->GetInt()); // accessing via dereference then function
        SomeFuncUsingIntValC(foo->GetInt()); // accessing via dereference then function

        // Is this better?
        int val = foo->GetInt();
        SomeFuncUsingIntValA(val);
        SomeFuncUsingIntValB(val);
        SomeFuncUsingIntValC(val);

        ///////////////////////////////////////////////
        // And likewise with . operator
        Foo fooDot(5);
        SomeFuncUsingIntValA(fooDot.GetInt()); // accessing via function
        SomeFuncUsingIntValB(fooDot.GetInt()); // accessing via function
        SomeFuncUsingIntValC(fooDot.GetInt()); // accessing via function

        // Is this better?
        int valDot = foo.GetInt();
        SomeFuncUsingIntValA(valDot);
        SomeFuncUsingIntValB(valDot);
        SomeFuncUsingIntValC(valDot);

        ///////////////////////////////////////////////
        // And lastly, a dot operator to a member, not a function
        SomeFuncUsingIntValA(fooDot.m_intVal); // accessing via member
        SomeFuncUsingIntValB(fooDot.m_intVal); // accessing via member
        SomeFuncUsingIntValC(fooDot.m_intVal); // accessing via member

        // Is this better?
        int valAsMember = foo.m_intVal;
        SomeFuncUsingIntValA(valAsMember);
        SomeFuncUsingIntValB(valAsMember);
        SomeFuncUsingIntValC(valAsMember);
    }

      

+3


source to share


3 answers


Ok, so I'm trying to find an answer here.

Short version: you definitely don't need to do this.

Long version: you may need to do this.

So here it is: in interpreted programs like Javascript

, these things can have a noticeable effect. In compiled programs like C ++, there isn't much to be found at all.

Most of the time, you don't need to worry about it, because a huge amount of resources have been spent on compiler optimization algorithms (and actual implementations) that the compiler will correctly decide what to do: allocate an extra register and store the result to reuse it or recalculate each times and save that register space, etc.



There are cases where the compiler cannot do this. That is, when he cannot prove that multiple calls give the same result. Then he has no choice but to make all the calls.

Now let's assume the compiler makes the wrong choice and you try micro-optimization as a precautionary measure. You are doing an optimization, and you are giving up a 10% performance increase (which is already overly optimistic for this kind of optimization) on that part of the code. But what do you know, your code only spends 1% of its time on this piece of code. The rest of the time is most likely wasted in several hot cycles and waiting for data to be fetched. Thus, you spend a lot of effort to optimize your code only to get an overall performance improvement of 0.1% overall, which will not even be observed due to external factors that change the execution time more than this amount.

So don't waste your time on micro-optimizations in C ++.

However, there are times when you might need to do this and even crazier things. But this only happens after you have properly profiled your code, and that's another discussion.

So worry about readability, don't worry about micro-optimization.

+4


source


The question has nothing to do with operators ->

and .

, but rather repeated expressions in general. Yes, it is true that most modern compilers are smart enough to optimize code that evaluates the same expression multiple times (assuming it has no observable side effects).

However, using an explicit intermediate variable usually makes the program more readable because it explicitly exposes the fact that the same value is supposed to be used in all contexts. It reveals the fact that you intend to use the same meaning in all contexts.



If you repeat using the same expression to generate this value over and over, this fact becomes much less obvious. First, it is difficult to tell at first glance whether the expressions are actually identical (especially when they are long). Second, it is not obvious whether successive evaluations of an apparently same expression give the same results.

Finally, slicing up long expressions into smaller ones using intermediate variables can greatly simplify the debugging of code in the step-by-step debugger, since it gives the user a much greater degree of control with the step-forward and step-over commands.

+3


source


It is for sure better from a readability and maintainability standpoint to have such a temporary variable.

From a performance standpoint, you shouldn't worry about such micro-optimization at this stage (premature optimization). What's more, modern C ++ compilers can still optimize, so you really shouldn't worry about that.

+1


source







All Articles