令人惊讶的糟糕的 c# 开关性能 [英] Surprisingly bad c# switch performance
问题描述
我创建了一个 TypeSwitch
类来使用类似于以下简化示例的代码来转换我的字段:
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;
}
}
使用instrumentation工具,我看到超过60%的时间都花在了上面ConvertFromDBValue
的函数体中,即我花在switch(或try-catch)上的时间比查找的时间多Dictionary.get_Item
中的 Type 值并转换该值(例如 Convert.ToInt32
).实际上,我在函数体中花费的时间是 Dictionary.get_Item
...
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
...
这让我有点惊讶 - 谁能确认 switch
慢得多,或者还有其他原因吗?!
This is somehow surprising to me - can anyone confirm the switch
is so much slower, or is there any other reason for this?!
更新我删除了 try-catch 部分,但这并没有太多......
UPDATE I removed the try-catch part but this didn't really much...
推荐答案
正如其他人提到的,您正在支付装箱/拆箱罚款,如果可能,您应该尝试消除它.
As others mentioned you are paying the boxing/unboxing penalty and if possible you should try to eliminate it.
至于方法的主体,您应该摆脱字典并使用 if if elses
链 - 它应该可以显着提高性能.
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.
编辑
在 Hogan 发表评论后,我对其进行了测试,令我惊讶的是 dict+catch 与 if 之间的差异在 5%-15% (如果稍微快一点)在我下面的不完美测试中.
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
测试:
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();
}
}
这篇关于令人惊讶的糟糕的 c# 开关性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!