Search code examples
c#.netperformance

How can I Improve the Performance of comparing large Structs in C#?


I want to compare a large Struct in C# via Equals, which in turn is implemented through IEquatable<T> Interface.
My Problem is that it performs very poorly, as my struct is rather large. Imagine a simplified version of the struct like the follwoing:

public struct Data
{
    public byte b0;
    public byte b1;
    public byte b2;
    public byte b3;
    public byte b4;
    public byte b5;
    public byte b6;
    public byte b7;
}

I would now write a simple Equals:

public bool Equals(Data other)
{
    return b0 == other.b0 &&
           b1 == other.b1 &&
           ... 
}

Is there a way to make the Equals Method more efficient?

Update

My struct type is in unmanaged according to the definition given here.


Solution

  • There in fact is a way to improve the performance of complex structs.

    Taking your example:

    public struct Data
    {
        public byte b0;
        public byte b1;
        public byte b2;
        public byte b3;
        public byte b4;
        public byte b5;
        public byte b6;
        public byte b7;
    }
    

    Then you could write your Equals Method as follows:

    public bool Equals(Data other)
    { 
        DatHelper helperThis = Unsafe.As<Data, DatHelper>(ref this); 
        DatHelper helperOther = Unsafe.As<Data, DatHelper>(ref other); 
        return helperThis.Equals(helperOther); 
    }
    

    With DataHelper looking somewhat like this:

    public struct DataHelper
    {
        public long l0;
        
        public bool Equals(DataHelper other)
        {
            return l0 == other.l0; 
        }
    }
    

    Why does this work?

    Well the important thing here is, that both structs have the same size in memory. We can then use Unsafe.As to reinterpret the memory of Data as DataHelper, which allows us to compare, in our case, one long with one long, instead of eight bytes with eight bytes.

    This is extensible. If you have for instance a struct, which is 33 bytes in size, then you could create DataHelper as four longs and one byte and compare them.

    The only things that must be guaranteed are:

    1. You must use a struct. This won't work with a class, as classes only contain the location of their data, while structs contain their actual data.
    2. The size of your two structs must be the same.

    As always, measure whether or not you actually see a performance gain.