Surprisingly bad c# switch performance

偶尔善良 提交于 2019-12-25 08:23:40

问题


I have created a TypeSwitch class to cast my fields using a code similar to the following shortened sample:

static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>() 
{
    {typeof(Int16), 1},
    {typeof(Int32), 2},
    {typeof(Int64), 3},
    {typeof(IntPtr), 4},
    ...
    {typeof(String), 18}
};

public static object ConvertFromDBValue(Type type, object value) 
{
    try 
    {
        switch (TypeDefs[type]) 
        {
            case 1: // {typeof(Int16), 1},
            {
                return Convert.ToInt16(value);
            }
            case 2: // {typeof(Int32), 2},
            {
                return Convert.ToInt32(value);
            }
            case 3: // {typeof(Int64), 3},
            {
                return Convert.ToInt64(value);
            }
            ...
            ...
            ...
            case 17: // {typeof(Char), 17},
            case 18: // {typeof(String), 18},
            {
                return value.ToString().Trim();
            }
            default: 
            {
                return value;
            }
        }
    }
    catch (Exception ex) 
    {
        throw ex;
    }
}

Using instrumentation tool, I see over 60% of the time is spent in the function body of the above ConvertFromDBValue, i.e. I spend more time because of switch (or try-catch) than look up the Type value in Dictionary.get_Item and converting the value (e.g. Convert.ToInt32). Actually, I spend 3 times more time in the function body than the Dictionary.get_Item...

This is somehow surprising to me - can anyone confirm the switch is so much slower, or is there any other reason for this?!

UPDATE I removed the try-catch part but this didn't really much...


回答1:


As others mentioned you are paying the boxing/unboxing penalty and if possible you should try to eliminate it.

As for the body of the method you should get rid of the dictionary and use a chain of if if elses - it should give you a measurable improvement in performance.


EDIT

After Hogan's comment I've tested it and to my surprise the difference between dict+catch vs if is less was between 5%-15% (if was slightly faster) in my imperfect test below.

Dict+case: 987.0945ms
Ifs: 937.5104ms
Hogan's array of funcs: 854.4887ms

Test:

class Program
{
    static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>()
    {
        {typeof(Int16), 1},
        {typeof(Int32), 2},
        {typeof(Int64), 3},
        {typeof(IntPtr), 4},
        {typeof(char), 5},
        {typeof(String), 6}
    };

    static KeyValuePair<Type,object>[] _Types = new[] 
        { new KeyValuePair<Type,object> ( typeof(Int16),5 ),
        new KeyValuePair<Type,object> (typeof(Int32),57 ),
        new KeyValuePair<Type,object> (typeof(Int64),157 ),
        new KeyValuePair<Type,object> (typeof(IntPtr),new IntPtr(6) ),
        new KeyValuePair<Type,object> (typeof(String),"Hello!" ),
    };

    public static object ConvertFromDBValue(Type type, object value)
    {
        try
        {
            switch (TypeDefs[type])
            {
                case 1: // {typeof(Int16), 1},
                    {
                        return Convert.ToInt16(value);
                    }
                case 2: // {typeof(Int32), 2},
                    {
                        return Convert.ToInt32(value);
                    }
                case 3: // {typeof(Int64), 3},
                    {
                        return Convert.ToInt64(value);
                    }
                case 4: // {typeof(IntPtr), 4},
                    {
                        return value;
                    }
                case 5: // {typeof(Char), 17},
                case 6: // {typeof(String), 18},
                    {
                        return value;
                    }
                default:
                    {
                        return value;
                    }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public static object ConvertFromDBValue2(Type type, object value)
    {
        try
        {
            if (type == typeof(Int16))
            {
                return Convert.ToInt16(value);
            }
            if (type == typeof(Int32))
            {
                return Convert.ToInt32(value);
            }
            if (type == typeof(Int64))
            {
                return Convert.ToInt64(value);
            }
            if (type == typeof(IntPtr))
            {
                return (IntPtr)value;
            }
            if (type == typeof(Char) || type == typeof(String))
            {
                return value.ToString().Trim();
            }
            return value;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }


    static Func<object, object>[] funcList =
    {
        (value) => value,                    //0
        (value) => Convert.ToInt16(value),   //1
        (value) => Convert.ToInt32(value),   //2
        (value) => Convert.ToInt64(value),   //3
        (value) => value,   //4
        (value) => value,   //5
        (value) => value,   //6
        (value) => value,   //7
        (value) => value,   //8
        (value) => value,   //9
        (value) => value,   //10
        (value) => value,   //11
        (value) => value,   //12
        (value) => value,   //13
        (value) => value,   //14
        (value) => value,   //15
        (value) => value,   //16
        (value) => value,   //17
        (value) => value.ToString().Trim() //18
    };

    public static object ConvertFromDBValueHogan(Type type, object value)
    {
    return funcList[TypeDefs[type]](value);
    }

    static void Main(string[] args)
    {
        var sw = new System.Diagnostics.Stopwatch();
        Random random = new Random(113453113);


        sw.Start();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p = ConvertFromDBValue(testValue.Key, testValue.Value);
        }
        var elapsed = sw.Elapsed;
        Console.WriteLine($"Dict+case: {elapsed.TotalMilliseconds}ms");

        sw.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p2 = ConvertFromDBValue2(testValue.Key, testValue.Value);
        }
        elapsed = sw.Elapsed;
        Console.WriteLine($"Ifs: {elapsed.TotalMilliseconds}ms");

        sw.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p3 = ConvertFromDBValueHogan(testValue.Key, testValue.Value);
        }
        elapsed = sw.Elapsed;
        Console.WriteLine($"Hogan's array of funcs: {elapsed.TotalMilliseconds}ms");
        Console.ReadLine();
    }
}



回答2:


Here is some sample code if my "array of functions and array index to pick" was not clear.

Func<object, object>[] funcList =
   { (value) => value,                    //0
     (value) => Convert.ToInt16(value),   //1
     (value) => Convert.ToInt32(value),   //2
     (value) => Convert.ToInt64(value),   //3
     (value) => value,   //4
     (value) => value,   //5
     (value) => value,   //6
     (value) => value,   //7
     (value) => value,   //8
     (value) => value,   //9
     (value) => value,   //10
     (value) => value,   //11
     (value) => value,   //12
     (value) => value,   //13
     (value) => value,   //14
     (value) => value,   //15
     (value) => value,   //16
     (value) => value,   //17
     (value) => value.ToString().Trim() //18
   }; 

public static object ConvertFromDBValue(Type type, object value)
{
  if (TypeDefs[type] <= 18)
    return funcList[TypeDefs[type]](value);
  else
   return value;
}

To make this even faster take out the if (TypeDefs[type] <= 18) if statement if you can guarantee there are no values greater than 18.

This is sample non-tested code.



来源:https://stackoverflow.com/questions/40812177/surprisingly-bad-c-sharp-switch-performance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!