2016-11-25 262 views
0

我创建了一个TypeSwitch类使用类似于下面的缩短样品代码投我的领域:令人惊讶的糟糕C#开关性能

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; 
    } 
} 

使用仪器仪表工具,我看的时候超过60%是花在上面的ConvertFromDBValue的函数体,即我花费更多的时间因为切换(或try-catch)而不是查找Dictionary.get_Item中的Type值并转换该值(例如Convert.ToInt32)。实际上,我在功能体中花费的时间比Dictionary.get_Item多3倍..​​.

这对我来说有些令人惊讶 - 任何人都可以确认switch慢得多,或者是否有其他原因?

UPDATE 我删除的try-catch一部分,但是这并没有真的多...

+0

我怀疑它的'之开关,减慢你失望。我怀疑这是需要时间的价值的拳击。如果你的字典是强类型的,那么它将工作得非常快。 –

+0

交换机应该是很快的。这取决于。你称这种方法有多少次?不要一遍又一遍地使用try-catch,因为这太慢了。 –

+1

BTW :(与你的性能问题无关)像Dictionary >这样的字典可以是一个简洁的解决方案,它可以避免使用* switch * .... **'Dictionary > TypeDefs = new Dictionary >(){typeof(Int16),x => Convert.ToInt16(x)}, {typeof(String), x => x.ToString()。Trim()} }; var val = TypeDefs [typeof(short)](“12345”);'** –

回答

-1

下面是一些示例代码,如果我“的功能和数组索引挑阵”是不明确的。

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; 
} 

为了使这个更快的if语句拿出if (TypeDefs[type] <= 18)如果你能保证不存在值大于18

这是样品未测试的代码。

+3

'TypeDefs + funcList'只是为了得到一个委托?为什么不简单地在我的评论中使用字典作为一个说法呢? 'var val = TypeDefs [typeof(short)](“12345”)' –

+0

实际上是一个反投票,因为有两个字典访问调用这个方法...... – neggenbe

+0

@neggenbe - 任何优化器只会在这里打一个电话。 – Hogan

0

正如其他人提到你正在支付拳击/拆箱罚款,如果可能你应该尝试消除它。

至于方法的主体,你应该摆脱字典,并使用链如果if elses - 它应该给你一个可衡量的性能提高。


编辑

后霍根的评论我测试过它,让我吃惊的差异字典+抓之间VS 如果小于5%之间-15%(如果是稍微快一点)在我下面的不完美测试中。

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(); 
    } 
} 
+0

好吧,我会尝试 - 我明白了 - 对于列表末尾的项目,“if-else”速度较慢,但​​字典访问速度更快......至于拳击,我打开了一个关于它的新问题[在这里](http://stackoverflow.com/questions/40812841/improve-efficiency-of-database-row-reading-without-boxing)... – neggenbe

+0

这是错误的字典应该比chained更快,如果其他和切换 - 除非你了解频率。 – Hogan