Wednesday

Performance Comparison of Flag Enumeration with Bitwise Operations in C#

The compile-able code worth a thousand words; Copy the following code in your own project to see which technique of bitwise operations on enumerations are best suitable for your needs.

using System.Diagnostics;
using System;

namespace EnumApp
{
    public enum Planets
    {
        //Start with zero and increment by the power of 2
        //Make sure you assign explicit values or you will get 
        //weired results with bitwise operations
        //e.g 00001000 | 00010000 = 00011000
        Undefined = 0, //00000000
        Mercury = 1,   //00000001
        Venus = 2,     //00000010
        Earth = 4,     //00000100
        Mars = 8,      //00001000
        Jupiter = 16   //00010000
    }

    //A static class which will hold the extension methods for 
    //type System.Enum
    //This way all the enums you will create in your project 
    //will have following extension methods.
    public static class EnumBitwiseExtension
    {
        //Look at method UseExtendedApproach in EnumBitwise class to see
        //how to use following methods.
        public static bool Contains(this Enum type, T value)
        {
            return (((int)(object)type & (int)(object)value) == 
                (int)(object)value);
        }

        public static bool EqualTo(this Enum type, T value)
        {
            return (int)(object)type == (int)(object)value;
        }
    }

    public class EnumBitwise
    {
        //This method demonstrates the most simple technique 
        //of using bitwise operations on enumerations
        //This technique of using bitwise operations can be 
        //used with any version of .NET framework
        //Following technique requires more code to be written
        //but delivers the best performance.
        public void UseClassicApproach()
        {
            //Setting a value
            Planets plnt1 = Planets.Earth;
            //Setting multiple values
            Planets plnt2 = 
                Planets.Earth | Planets.Jupiter | Planets.Mars;
            //Removing a value
            Planets plnt3 = plnt2 & ~Planets.Jupiter;

            bool isEarth = plnt1 == Planets.Earth;
            bool containsMars = (plnt1 & Planets.Mars) == Planets.Mars;
            bool containsEarthAndMars = 
                (plnt3 & Planets.Earth) == Planets.Earth 
                && 
                (plnt3 & Planets.Mars) == Planets.Mars;
        }

        //This method demonstrates how to use extension methods 
        //defined above for type Enum
        //This technique can be used with .NET 3.5 and higher
        public void UseExtendedApproach()
        {
            //Setting a value
            Planets plnt1 = Planets.Earth;
            //Setting multiple values
            Planets plnt2 = 
                Planets.Earth | Planets.Jupiter | Planets.Mars;
            //Removing a value
            Planets plnt3 = plnt2 & ~Planets.Jupiter;

            bool isEarth = plnt1.EqualTo(Planets.Earth);
            bool containsMars = plnt2.Contains(Planets.Mars);
            bool containsEarthAndMars = 
                plnt3.Contains(Planets.Earth | Planets.Mars);
        }

        //This method demonstrates how to use HasFlag 
        //method which is being made available by .NET framework
        //This technique can be used with .NET 4.0 and higher
        public void UseNativeApproach()
        {
            //Setting a value
            Planets plnt1 = Planets.Earth;
            //Setting multiple values
            Planets plnt2 = 
                Planets.Earth | Planets.Jupiter | Planets.Mars;
            //Removing a value
            Planets plnt3 = plnt2 & ~Planets.Jupiter;

            //Performance of HasFlag function has been made 
            //little better in .NET 4.5

            bool isEarth = plnt1.HasFlag(Planets.Earth);
            bool containsMars = plnt2.HasFlag(Planets.Mars);
            bool containsEarthAndMars = 
                plnt3.HasFlag(Planets.Earth | Planets.Mars);
        }

        //Time to do some benchmarking
        public void TestPerformance()
        {
            string result = string.Empty;
            
            Stopwatch a = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                UseExtendedApproach();
            }
            a.Stop();

            Stopwatch b = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                UseNativeApproach();
            }
            b.Stop();

            Stopwatch c = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                UseClassicApproach();
            }
            c.Stop();

            result = string.Format("UseClassicApproach: {0}ms; " +
                                   "UseExtendedApproach: {1}ms; " + 
                                   "UseNativeApproach: {2}ms", 
                                   c.ElapsedMilliseconds,
                                   a.ElapsedMilliseconds, 
                                   b.ElapsedMilliseconds 
                                   );

            //I got following results on my computer
            //UseClassicApproach: 3ms; 
            //UseExtendedApproach: 49ms; 
            //UseNativeApproach: 220ms 
        }
    }
}



No comments:

Post a Comment

Your comments are highly appreciated!