Readable code but performance issues ?

Hi together,

I have a generall question about performance/memory usage. Will creating of objects for better readable code lead into performance issues ?

Let´s say we have something like:

gameObject.GetComponentInChildren<Something>().GetComponent<Rectangle>().rect.position;

Normally i would split that to:

Rectangle r = s.GetComponent<Rectangle>();
Vector2 size = r.rect.postion;```

On that why it´s much easier to read and helps a lot for debugging. But if this is done in each script or for each object, could that lead into (performance) issues on any time ?
Or is it ok do do that, if the objects will be set to null at the end and the garbage collector is "fast" enough to clear it up ? 

Kind regards,
Sphinks

You should absolutely use the second stretched-out way of doing things, one step at a time.

The compiler is likely to generate exactly the same (or possibly even better!) code that way.

The first format is something I call “Hairy lines of code,” and it prevents you from successfully reasoning about what it is you are even doing. Here’s my take on hairy code:

http://plbm.com/?p=248

As for performance, I’ve never seen an issue from writing “good code.”

Remember: the ONLY way to tell if code is causing a problem is to attach the Profiler (Window → Analysis → Profiler) and see. Any other way is pure speculation and a waste of time.

3 Likes

Alright, thanks a lot for your response. Will read your post in the link on evening.

With the profiler I have nothing done/tested till now. Still a bit of a noob in unity and it´s more important that the code is working at the moment ;), but this will be for sure something to have a look in the future.

But i´m happy, that it should be done with the second way!

There is no measurable performance difference between the two examples you’ve shown.

What strikes me about this line:

Is the fact that it goes against the so-called law of demeter. A common issue in Unity, but things (classes, components, etc.) should have only limited knowledge about the web of interconnected systems. Ideally, they only talk to their closest friends. This makes it far easier to reason about code and usually prevents complex side-effects.

The shown code snippet assumes that Something always comes with a Rectangle component. If other systems also want to use the rect position, they need the same implicit knowledge. This is bad because it means that once you have a few places of duplicated code all assuming this specific setup, you cannot easily change it. And worse, it becomes hard to debug, since suddenly everything breaks if you move the Rectangle component to another GameObject, for example.

If Something has a rect position, it makes sense to introduce a facade method GetRectPosition, which for the start, can simply do GetComponent().rect.position, but this part is now encapsulated and centralized, hence can be changed more easily. It now becomes possible to change the Rectangle component. Also, now that this is centralized to one class, you can easily optimize the code and cache the call to GetComponent by storing the result in a member variable.

1 Like

I know what you mean, but each gameobject or at least UI component has a “public” RectTransform, so it´s actually always possible to access it with GetComponent/GetComponentInChildren from different places. I totally agree with you, to create a method which has access to it and use the method instead of direct accessing.

And that should only be an example for bad code with a long concatenation. It was just about, if clean/readable code could create performance issues.

Thanks for this tidbit!

Personally I just think it’s needlessly hard to read and reason about, particularly in the case of failure.

But the notion of “size of knowledge” is certainly a very valid one, as well as making tons of copy/pasta code that needs to be changed if a Something becomes a SomethingElse in the future.

I also like lotsa whitespace, basically ever since I learned that whitespace was free. :slight_smile:

1 Like

I would be using the second way because I would usually write it like this anyways:

Something s = gameObject.GetComponentInChildren<Something>();
if (s == null)
{
    Debug.LogError("Something not found in MyClass.MyMethod");
}
else
{
    Rectangle r = s.GetComponent<Rectangle>();
    if (r == null)
    {
        Debug.LogError("Rectangle not found in MyClass.MyMethod");
    }
    else
    {
        Vector2 size = r.rect.postion;
    }
}
2 Likes

Isn’t that just basic “use functions”? 50-year-old programming theory says to write a function “getHeadPosition()” with that long line in it. Now code is easier to read, and everyone who needs the head position can share that function which makes it mush easier to update when your models change.

Yeah, that’s certainly one way to centralize each piece of knowledge.

I think Xarb’s meaning was more on a line-by-line basis, each line only knowing one small part of the whole problem rather than some horrific thee.thou.them.thither.shall.smite(thee) type construct.

1 Like

Not exactly, I believe. Putting the long line as it stands in a function improves readability and might improve reuse but doesn’t change the dependencies in the system. In my understanding, the fancily-named law of demeter suggests improving the maintainability of the whole program by making dependencies short-leashed.

The original example was:

namespace Example1
{
    public class Player : MonoBehaviour
    {
        private void Start()
        {
            // This makes the intention more clear.
            Vector3 position = FindCellCenter();
        }

        public Vector3 FindCellCenter()
        {
            // And here we just put all the bad stuff in one place.
            // If we're lucky, this method can be reused and nothing
            // of Something or Rectangle changes, but usually,
            // we'll want to update these other components some day
            // and this here breaks.
             return gameObject
                .GetComponentInChildren<Something>()
                .GetComponent<Rectangle>()
                .rect
                .position;
        }
    }

    public class Something : MonoBehaviour
    {
    }

    public class Rectangle : MonoBehaviour
    {
        public Rect rect;
    }
}

The law of demeter version would be something like this:

namespace Example2
{
    public class Player : MonoBehaviour
    {
        private void Start()
        {
            // Clear intention.
            Vector3 position = FindCellCenter();
        }

        private Vector3 FindCellCenter()
        {
            // Here we have a single dependency.
            // Every Player needs its thing, but it can
            // be anything as long as it has a cell center.
            // Now, this can be reused in other classes
            // like AIPlayer or TestBot, which work totally different.
            return gameObject
               .GetComponentInChildren<Something>()
               .FindCellCenter();
        }
    }

    public class Something : MonoBehaviour
    {
        // Everything is a something if only it has a cell center.
        // Square and round things all have a center, for example.
        public Vector3 FindCellCenter()
        {
            // Well, this is a square thing, so get the Rectangle.
            return GetComponent<Rectangle>()
               .GetCellCenter();
        }
    }

    public class Rectangle : MonoBehaviour
    {
        private Rect rect;

        // Rectangles should always have a center.
        public Vector3 GetCellCenter()
        {
            // But not every rectangle needs to have a Unity rect as its data source.
            return rect.position;

            // Maybe our rectangle center is this:
            //return new Vector3(0, 0, 0);
            // because we only need it for a test class.
        }
    }
}

This is a lot more code and may not be needed in simple cases, but I still believe that as a general soft guideline, the rule guides developers into a direction that is more open to inserting new things or changing individual parts of a system.

And now comes the biggest disclaimer of any design pattern: If you’re not working in a team with multiple developers or the project is small, almost none of this matters, since architecture is all about managing large systems with developers stepping onto their feet. Basically, this makes sense if Dev A is in charge of creating Player, Enemy and Vehicle classes, where Dev B is working on the foundation of SomethingRound, SomethingSquare, and ThingThatDisappearsEveryThreeSecondsBecauseOurDesignersThoughtItWasCool.

I hope this doesn’t derail the original question too much, but I really enjoy discussing design patterns. :stuck_out_tongue:

Coming back to the performance question, I would apply the law of demeter to add variable caching during the optimization phase of the project like this:

public class Player : MonoBehaviour
{
    private Something something;

    private void Start()
    {
        Vector3 position = FindCellCenter();
    }

    private Vector3 FindCellCenter()
    {
        if (something == null)
            something = gameObject.GetComponentInChildren<Something>();

        return something.FindCellCenter();
    }
}

If the original code already separated everything into wrapper functions, such a change can be made without causing any conflicts with other systems (developers!), so it makes sense to do it right from the start, but optimization may only be needed once the project is close to release, where large refactorings become riskier and time is usually short anyway.

I looked at the Law of Demeter Wikipedia page, and it seems a big nothing. It even says that it’s just Loose Coupling, which is an older better-known term that has the advantage of being in plain English. It seems as if it was coined for a project named Demeter, died out with the project, then picked up again as a cool-sounded term.

That example has the idea sort of hidden. The point is that nowhere in the Player would you have the details of how a child finds it’s center (is it the center?). The Player finds it’s child, then calls a member function on it to get the data. If that takes 1 or 4 steps isn’t important. Sure, that fits into some Law of Demeter rule, but it’s also basic OOP design and it seems simpler to think of it that way.

To sum up, I’m sure you find thinking of LoD helpful, but other more common coding ideas have it completely covered.

1 Like