Question

Suppose I have this structures in c++:

class A{
  public:
    B b;
}

class B{
  public:
    C c;
}

class C{
  public:
    double x;
    double y;
    double z;
    double s;
    function Usize(){
      s  = sqrt(pow(x,2) + pow(y,2) + pow(z,2));
    }
}

Will accessing the the values in c ten times require more memory traffic than creating a direct pointer to c and use it? In code terms (assuming legal values):

double dx = 2*rand()-1;
double dy = 2*rand()-1;
double dz = 2*rand()-1;

a->b->c.x *= dx;
a->b->c.y *= dy;
a->b->c.z *= dz;

if (a->b->c.x > 10) a->b->c.x -= 10;
else if (a->b->c.x <0) a->b->c.x += 10;
if (a->b->c.y > 10) a->b->c.y -= 10;
else if (a->b->c.y < 0) a->b->c.y += 10;
if (a->b->c.z > 10) a->b->c.z -= 10;
else if (a->b->c.z < 0) a->b->c.z += 10;

a->b->c->Usize();

vs.

double dx = 2*rand()-1;
double dy = 2*rand()-1;
double dz = 2*rand()-1;


C* ac = a->b->c
ac.x *= dx;
ac.y *= dy;
ac.z *= dz;

if (ac.x > 10) ac.x -= 10;
else if (ac.x < 0)  ac.x += 10;
if (ac.y > 10) ac.y -= 10;
else if (Ac.y < 0) ac.y += 10;
if (ac.z > 10) ac.z -= 10;
else if (ac.z < 0) ac.z += 10;

Thanks.

Was it helpful?

Solution

In this exact case, a good compiler should be able eliminate the common expression and generate pretty much optimal code. Since you're accessing primitive types, a->b->c can be evaluated once and used throughout the method.

The call to C::USize() or access of a non primitive type in "class C" will break this pattern and force the compiler to re-evaluate a->b->c for the next line.

a->b->c.x = 10;
a->b->c.Usize();   // <-- Usize() may change a.b so the next line references another B.
a->b->c.y = 5;

This is because the compiler cannot make 100% sure that a method call/operator does not change a, a.b or b.c, so it has to re-evaluate the chain to make sure.

I'd almost call worrying about this at this stage premature optimization. That said, your second example is both more readable and helps the compiler not have to second guess in case you insert any method calls later, so I'd go for that.

OTHER TIPS

Chances are no. There will be no difference.

While it is true that dereference chaining will lead to more memory accesses, modern compilers are able to do exactly what you have done. (That is, transform your first example into your second example.)

This is due to a standard compiler optimization called Common Subexpression Elimination (CSE).

The name pretty much says it all. In your first example, a->b->c is the common subexpression that will be optimized out by the compiler. It will be evaluated only once, the result saved, and reused for all the instances it is needed.


There are a number of situations that could prevent a compiler from making such optimizations.

  1. If any relevant variables are declared volatile, then this optimization is not allowed since a volatile variable requires that it be reloaded each time it is used.
  2. If any of the relevant variables are (or potentially) modified, then this optimization is not allowed since it may produce a different result.

As a side-note though, your second example is also more readable since there's dereference chaining.
So if I had to pick which to use, I'd go with the second example anyway.

Theoretically it will not make a difference.

Any modern optimizer should translate to exactly the same code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top