2016-11-25 2 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 и преобразования значения (например, Convert.ToInt32). На самом деле, я трачу в 3 раза больше времени в функциональном теле, чем Dictionary.get_Item ...

Это как-то удивительно для меня - может ли кто-нибудь подтвердить, что switch настолько медленнее, или есть ли другие причины для этого ?!

UPDATE я удалил примерки поймать часть, но это не очень много ...

+0

Я сомневаюсь, что это 'switch', что замедляет работу. Я подозреваю, что это бокс стоимости, которая требует времени. Если ваш словарь строго типизирован, он будет работать очень быстро. –

+0

Переключатель работает быстро, как следует. это зависит. сколько раз вы называете этот метод? не используйте повторный захват снова и снова, потому что это медленный путь. –

+1

BTW: (Не связано с вашей проблемой производительности). Словарь типа «Словарь <Тип, Func <объект, объект >>» может быть более простым решением, которое исключает использование * switch * .... ** 'Dictionary > TypeDefs = новый словарь >() { {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 (TypeDefs[type] <= 18), если заявление, если вы можете гарантировать, нет значения больше, чем 18.

Это образец не протестированного кода.

+3

'TypeDefs + funcList', чтобы получить делегат? почему бы просто не использовать словарь, как сказано в моем комментарии? 'var val = TypeDefs [typeof (short)] (« 12345 »)' –

+0

Даун-голос на самом деле, так как есть два вызова доступа к словарю с этим методом .... – neggenbe

+0

@neggenbe - любой оптимизатор будет делать только один звонок здесь , – Hogan

0

Как уже упоминалось, вы платите штраф за бокс/unboxing, и, если возможно, вы должны попытаться его устранить.

Что касается тела метода, вы должны избавиться от словаря и использовать цепочку, если if elses - это должно дать вам ощутимое улучшение производительности.


EDIT

После комментария Хогана я проверил это и к моему удивлению, разница между Dict + уловом против если меньше было от 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

Это неправильно, словарь должен быть быстрее, чем цепочка if-else и переключаться - если вы не знаете что-то о частоте. – Hogan

Смежные вопросы