why are Unity's structs so fast?

Hi,
I’m finishing one of my projects and am down to microtuning the performance. I work a lot with hashtables and lists, so I thought I would replace the Vector2 with a custom struct, because I really only need the x and y coordinates to be saved in rather short ints.

So what I did was, I wrote this simple struct :

public struct Vector2Int  { 
    public int x;
    public int y;


    public Vector2Int(int x1, int y1)
    {
        x = x1;
        y = y1;
    }

    public static Vector2Int operator +(Vector2Int c1, Vector2Int c2)
    {
        return new Vector2Int(c1.x + c2.x, c1.y+c2.y);
    }

    public static Vector2Int operator -(Vector2Int c1, Vector2Int c2)
    {
        return new Vector2Int(c1.x - c2.x, c1.y - c2.y);
    }

    public override int GetHashCode() // for dictionaries
    {
        string hash = x + ":" + y;
        return hash.GetHashCode();
    }


    public static implicit operator Vector2Int(Vector2 o )
    {
        var a = new Vector2Int((int)o.x, (int)o.y);
        return a;
    }


}

The code I am refining at the moment is this part :

...
        Vector2Int chunkI = new Vector2Int(x, y); // get chunk index/index key

        t1 = Time.realtimeSinceStartup;
        if (!layer.chunks.TryGetValue(chunkI,out chunk))
        {
            chunk = new MT2DChunk2(layer);
            layer.chunks.Add(chunkI, chunk);
        }
        t += Time.realtimeSinceStartup - t1;
...

This part is looped 128x128 times. With the default Vector2 for the chunkI it takes 0.095 seconds in average. With my custom struct it takes 0.6 seconds. What’s happening, how come the original Vector2 struct is much faster even though it’s larger?

Thanks,
Sven

1 Like

That is a considerable difference … Did you test a similar custom struct without doing the float to int cast? Just wondering as Unity Vector2 creation does not need to cast the data types as you are currently doing . .?

One thing that stands out is that your GetHash function uses string concatenation, which causes serious memory thrashing. Do you need the GetHashCode function at all? If you do, try using xor instead:

(~x).GetHashCode() ^ y.GetHashCode()

May not be the issue, but could help ;).

1 Like

Ints and floats are both 32 bits; the Vector2 struct is not larger than your Vector2Int struct. That said, my project has an Int2 struct which is similar to yours (because I specifically don’t want floats, not because of optimizing), and according to a quick benchmark I did for creating and adding a bunch of Int2 vs. Vector2, isn’t really any slower. So I would assume the difference is the GetHashCode function like tomvds said.

–Eric

Thanks, the main problem really was the GetHash function. Now that I did it like tomvds suggested I’m down to 100-110 ms. It is still noticably slower, so maybe there’s more to it than just the one thing.
@Eric5h5
Vector2 also contains the variables “normalized” and “magnitude”, and even though they are probably not real references but are rather calculated once get is called, it still makes it considerably larger in my mind.

@shaderbytes
the x and y are always ints. The cast you see in my struct is actually an implicit cast from Vector2 to Vector2Int, in which typecasting does need to occur.

I don’t know if the hashcode I suggested is all that good, which could cause it to be slower than the vector2 one.

Note that normalized and magnitude are properties, which is functions that pretend to be variables. They are calculated every time you call them and do not store any data in the struct.

No, Vector2 structs are 64 bytes (32 bits for each float). Only the x and y values are stored. If the only reason you have your Vector2Int struct is because of supposed optimization, don’t bother.

–Eric

ok, thank you for your help, I will switch to just one int instead then. I really need the performance :smile: But seeing that there’s little magical about the unity structs was definitely worth asking for me :slight_smile: