Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

why are Unity's structs so fast?

Discussion in 'Scripting' started by kiriri, Nov 18, 2013.

  1. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    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 :

    Code (csharp):
    1. public struct Vector2Int  {
    2.     public int x;
    3.     public int y;
    4.  
    5.  
    6.     public Vector2Int(int x1, int y1)
    7.     {
    8.         x = x1;
    9.         y = y1;
    10.     }
    11.  
    12.     public static Vector2Int operator +(Vector2Int c1, Vector2Int c2)
    13.     {
    14.         return new Vector2Int(c1.x + c2.x, c1.y+c2.y);
    15.     }
    16.  
    17.     public static Vector2Int operator -(Vector2Int c1, Vector2Int c2)
    18.     {
    19.         return new Vector2Int(c1.x - c2.x, c1.y - c2.y);
    20.     }
    21.  
    22.     public override int GetHashCode() // for dictionaries
    23.     {
    24.         string hash = x + ":" + y;
    25.         return hash.GetHashCode();
    26.     }
    27.  
    28.  
    29.     public static implicit operator Vector2Int(Vector2 o )
    30.     {
    31.         var a = new Vector2Int((int)o.x, (int)o.y);
    32.         return a;
    33.     }
    34.  
    35.  
    36. }
    The code I am refining at the moment is this part :
    Code (csharp):
    1.  
    2. ...
    3.         Vector2Int chunkI = new Vector2Int(x, y); // get chunk index/index key
    4.  
    5.         t1 = Time.realtimeSinceStartup;
    6.         if (!layer.chunks.TryGetValue(chunkI,out chunk))
    7.         {
    8.             chunk = new MT2DChunk2(layer);
    9.             layer.chunks.Add(chunkI, chunk);
    10.         }
    11.         t += Time.realtimeSinceStartup - t1;
    12. ...
    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
     
    Ali_V_Quest likes this.
  2. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    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 . .?
     
  3. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    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:
    Code (csharp):
    1. (~x).GetHashCode() ^ y.GetHashCode()
    May not be the issue, but could help ;).
     
    Last edited: Nov 18, 2013
    idbrii likes this.
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    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
     
  5. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    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.
     
    Last edited: Nov 18, 2013
  6. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    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.
     
  7. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    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
     
  8. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    ok, thank you for your help, I will switch to just one int instead then. I really need the performance :D But seeing that there's little magical about the unity structs was definitely worth asking for me :)