When adding two Vector3s and assigning to an existing variable, does Unity do the addition direct...

When adding two Vector3s and assigning to an existing variable, does Unity do the addition direct to the existing variable, or create a new one?

For example:

Vector3 v1, v2, v3;
v1 = v2+v3;

What would Unity be doing internally? Would it be:

v1.x = v2.x+v3.x
v1.y etc, etc

Or:

Vector3 temp = new Vector3();
temp.x = v2.x+v3.x
temp.y etc, etc
v1 = temp

Sorry I know it’s a niche question! I’m doing some optimisation and wondering if it’s worth inlining all my vector math.

This occurs in internal Unity C++ code that the Unity dev’s are not likely to share with us, but these guys are professionals so I’m sure they do it in a way that is both well optimized and universally acceptable.

My best guess is they do this:
v1 = new Vector3(v2.x + v3.x, v2.y + v3.y, v2.z + v3.z);

I would advise against trying to optimize to this level unless vector addition is something you do A LOT EVERY FRAME. You will often be sacrificing readability (and therefore your ability to easily modify your code) for an optimization that literally doesn’t make your framerate or anything any faster because of how small the gain is.

I would advise trying to focus more on multi-step functions or operations in your code and reducing the number of steps as opposed to making each individual step as optimized as possible.

Best of luck achieving the results you’re after!

Profile before optimizing. Make sure your optimization time is spent in all the right places.

1 Like

Optimising is going well. Got GC down to 0 which was the goal for today :slight_smile:

Vectors are structs and therefore don’t generate garbage, unless you explicitly box them in some manner.

Vector3’s are C#/.Net structs, and therefore are not actually performed on the C++ side of the engine.

Here is the source for the Vector3 addition operator (decompiled for UnityEngine.dll):

    public static Vector3 operator +(Vector3 a, Vector3 b)
    {
      return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
    }

Effectively when you say:

v1 = v2 + v3;

You’re really saying:

v1 = Vector3.Add(v2, v3);

So, the addition operator method is loaded up onto the stack, copies of v2 and v3 are passed in. This method then calls the struct constructor passing the in the sum of each vector component, this will technically allocate yet another method onto the stack to then set the fields of said struct (as for the mono runtime optimizing struct constructors… we’re not going to get into all that). Finally that new Vector3 is returned and set to v1.

For optimizations sake it is technically most efficient to inline addition as:

v1.x = v2.x + v3.x;
v1.y = v2.y + v3.y;
v1.z = v2.z + v3.z;

Though if you’re seeing performance issues at this level of things… you got bigger problems.

The only places I would say you should inline vector addition is if you have some loop where you have to sum thousands of vectors. For example this:

Vector3[] arr = *An array of thousands of Vectors*;
Vector3 result = Vector3.zero;
for(int i = 0; i < arr.Length; i++)
{
    result += arr[i];
}

vs

Vector3[] arr = *An array of thousands of Vectors*;
Vector3 result = Vector3.zero;
for(int i = 0; i < arr.Length; i++)
{
    result.x += arr[i].x;
    result.y += arr[i].y;
    result.z += arr[i].z;
}

Which honestly though the difference still isn’t much. On my machine we’re talking a difference of 4ms vs 1.5ms for an array of 100,000 vectors. Which again… you have bigger issues here than the addition operator. Why are you looping and summing 100,000 vectors in an array? Maybe this expensive job should be threaded, rather than optimized via the operator.

With that said… Unity’s implementation of the addition operator uselessly requires 2 stack layers. It would technically been better for them to say:

public static Vector3 operator +(Vector3 a, Vector3 b)
    {
        a.x += b.x;
        a.y += b.y;
        a.z += b.z;
        return a;
    }

But eh, who am I to complain.

Good points here from everybody, but don’t forget when IL2CPP transmogrifies all this IL code, then the C++ compiler on your target platform gets done with it, good luck predicting what any of it actually compiles down to.

Measure first. THEN optimize where it makes sense.

1 Like