Home > Blockchain >  What is the best and performant way to compare enums in c#?
What is the best and performant way to compare enums in c#?

Time:12-28

I am struggling with comparing enums. I have tested 5 different way(Equal, HasFlag, == operator , bitwise operator and is operator). Two of them(HasFlag and Equal) are not good way to use for comparing two enums. But other three ways are confusing me. I was trying to benchmark them but they are giving to me various results. Here the test methods.

[Flags]
public enum Typess
{
    First = 2,
    Second = 4,

public int testCount = 1000;
public int sampleCount = 10000000;

GC.Collect();
var stopwatch = new Stopwatch();
float avarageTime1 = 0f;

for (int i = 0; i < testCount; i  )
{
    stopwatch.Reset();
    stopwatch.Start();
    for (int j = 0; j < sampleCount; j  )
    {
        TestEnum(Typess.First);   
    }
    stopwatch.Stop();
    avarageTime1  = stopwatch.ElapsedMilliseconds;
}
Debug.Log(" Enum Test Consumed time : "   avarageTime1 / (float)testCount   " ms");

GC.Collect();
float avarageTime2 = 0f;
for (int i = 0; i < testCount; i  )
{
    stopwatch.Reset();
    stopwatch.Start();
    for (int j = 0; j < sampleCount; j  )
    {
        TestEnumByte(Typess.First);   
    }
    stopwatch.Stop();
    avarageTime2  = stopwatch.ElapsedMilliseconds;
}
Debug.Log("Enum Byte Test Consumed time : "   avarageTime2 / (float)testCount    " ms");

GC.Collect();
float avarageTime3 = 0f;
for (int i = 0; i < testCount; i  )
{
    stopwatch.Reset();
    stopwatch.Start();
    for (int j = 0; j < sampleCount; j  )
    {
        TestEnumIs(Typess.First);   
    }
    stopwatch.Stop();
    avarageTime3  = stopwatch.ElapsedMilliseconds;
}
Debug.Log("Enum Is Test Consumed time : "   avarageTime3 / (float)testCount    " ms");

private bool TestEnum(Typess testType) => testType == Typess.Second;
private bool TestEnumByte(Typess testType) => (testType & Typess.Second) != 0;
private bool TestEnumIs(Typess testType) => testType is Typess.Second;

Why these three methods giving me various result? Am I doing something wrong? Thanks

CodePudding user response:

Let's improve your test harness:

IEnumerable<TimeSpan> GenerateTestRuns(Func<Typess, bool> f, int samples)
{
    while (true)
    {
        int generation = GC.GetGeneration(0);
        Stopwatch stopwatch = Stopwatch.StartNew();
        for (int j = 0; j < samples; j  )
        {
            f(Typess.First);
        }
        stopwatch.Stop();
        if (generation == GC.GetGeneration(0))
        {
            yield return stopwatch.Elapsed;
        }
    }
}

The beauty of this code is that it will produce zero or more runs of the test where there were no garbage collections. The downside is that if there are always garbage collections this code will be stuck in an infinite loop.

It worked fine and always produced values in this case.

Here are the tests I ran:

Dictionary<string, Func<Typess, bool>> fs = new Dictionary<string, Func<Typess, bool>>()
{
    { "testType == Typess.Second", testType => testType == Typess.Second },
    { "(testType & Typess.Second) != 0", testType => (testType & Typess.Second) != 0 },
    { "testType is Typess.Second", testType => testType is Typess.Second },
};

Now the code to actually run the tests is this:

int testCount = 1000;
int sampleCount = 10000000;

Dictionary<string, TimeSpan> results = fs.ToDictionary(x => x.Key, x => TimeSpan.Zero);

for (int i = 0; i < testCount; i  )
{
    foreach (KeyValuePair<string, Func<Typess, bool>> kvp in fs)
    {
        foreach (TimeSpan ts in GenerateTestRuns(kvp.Value, sampleCount).Take(1))
        {
            results[kvp.Key]  = ts;
        }
    }
}

foreach (KeyValuePair<string, TimeSpan> kvp in results)
{
    Console.WriteLine($"{kvp.Key} each run in {kvp.Value.TotalMilliseconds / testCount} ms");
}

I ran using optimized code and I checked that the IL was calling the functions.

The results I got were fairly consistent:

(1)

testType == Typess.Second each run in 21.3049772 ms
(testType & Typess.Second) != 0 each run in 21.321223999999997 ms
testType is Typess.Second each run in 26.525582 ms

(2)

testType == Typess.Second each run in 20.9722699 ms
(testType & Typess.Second) != 0 each run in 20.9309871 ms
testType is Typess.Second each run in 26.0593333 ms

CodePudding user response:

== operator: This operator compares the values of two enum variables and returns true if they are equal, or false if they are not. This is the recommended way to compare two enum values, as it is fast and efficient.

Bitwise operator: You can use the bitwise & operator to compare enum values that are decorated with the [Flags] attribute. This operator checks if the first enum value contains all the flags in the second enum value.

is operator: The is operator checks if an object is of a particular type. It can be used to check if an enum value is of a specific enum type, but it is not recommended to use it for comparing two enum values, as it is slower and less efficient than the other options. Additional type checking is performed.

In general, the == operator is the most efficient way to compare enum values, followed by the bitwise & operator for [Flags] enum values. The Equal method and is operator should generally be avoided for comparing enum values due to their lower performance.

  • Related