2016-06-21 4 views
4

Моей цели состоит в написании слабо типизированногоTryParse метода , что происходит в основном поддерживает все доступные типы структуры (Int, длинный, поплавок ...)Производительность метода отражение вызова против делегата вызова

public static bool TryParse(Type type, string s, out object obj) 

Реализация будет вызывать метод TryParse предоставленного аргумента type (если тип int, int.TryPase будет вызываться, а выходное значение будет возвращено как объект).

Я реализовал это с помощью отражения, но есть основные потери производительности (как я ожидал)

Отражение реализации:

public static class ParserHelper 
{ 
    public delegate bool TryParseDl(string str, out object obj); 

    private static readonly HashSet<Type> ParsableStructs = new HashSet<Type> 
    { 
    typeof(int), 
    typeof(uint), 
    typeof(decimal), 
    typeof(short), 
    typeof(ushort), 
    typeof(double), 
    typeof(long), 
    typeof(ulong), 
    typeof(float), 
    typeof(byte), 
    typeof(sbyte) 
    }; 

    public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers; 

    static ParserHelper() 
    { 
     StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs()); 
    } 

    /// Creates parsers for structs 
      private static Dictionary<Type, TryParseDl> CreateParsersForStructs() 
     { 
      var parsers = new Dictionary<Type, TryParseDl>(); 
      foreach (var t in ParsableStructs) 
      { 
       parsers[t] = GetParserForStruct(t); 
      } 
      return parsers; 
     } 

    private static TryParseDl GetParserForStruct(Type targetType) 
     { 
      var methodInfo = targetType.GetMethod(
       "TryParse", 
       BindingFlags.Public | BindingFlags.Static, 
       Type.DefaultBinder, 
       new[] { typeof(string), targetType.MakeByRefType() }, 
       null); 

      return (string str, out object obj) => 
       { 
        if (string.IsNullOrEmpty(str)) 
        { 
         obj = targetType.IsValueType ? Activator.CreateInstance(targetType) : null; 
         return true; 
        } 
        var inputParameters = new object[] { str, null }; 
        var tryParseResult = (bool)methodInfo.Invoke(null, inputParameters); 
        obj = inputParameters[1]; 
        return tryParseResult; 
       }; 
     } 
} 

А вот тест производительности:

public class Program 
{ 
    public static void Main() 
    { 
     Stopwatch s = new Stopwatch(); 
     string str = "100";  
     s.Start(); 
     for(int j = 0;j<100;j++) 
     { 
      int i; 
      int.TryParse(str,out i); 

     } 
     s.Stop(); 
     Console.WriteLine(s.Elapsed); 
     s.Reset(); 
     s.Start(); 
     var parser = ParserHelper.StructParsers[typeof(int)]; 
     for(int j = 0;j<100;j++) 
     {       
      object o; 
      parser(str, out o); 
     } 

     s.Stop(); 
     Console.WriteLine(s.Elapsed); 
    } 
} 

Средний результат: вызов отражения - бой в 200 раз медленнее, чем прямой вызов (на 100 попыток). Fiddle that demonstrates the reflection test

Я пытался улучшить производительность, используя кэшированные делегат:

public static class StructParserExtensions 
{ 
    public static bool IntToObjParse(string str, out object obj) 
    { 
     int i; 
     var result = int.TryParse(str, out i); 
     obj = result ? (object)i : null; 
     return result; 
    } 

    public static bool LongToObjParse(string str, out object obj) 
    { 
     long i; 
     var result = long.TryParse(str, out i); 
     obj = result ? (object)i : null; 
     return result; 
    } 

    //implementations for other types goes here 

} 


public static class ParserHelper 
{ 
    public delegate bool TryParseDl(string str, out object obj); 

    public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers; 

    static ParserHelper() 
    { 
     StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs()); 
    } 

    /// Creates parsers for structs 
    /// </summary> 
    /// <returns>Dictionary</returns> 
    private static Dictionary<Type, TryParseDl> CreateParsersForStructs() 
    { 

     var parsers = new Dictionary<Type, TryParseDl>(); 
     parsers[typeof(int)] = StructParserExtensions.IntToObjParse;      
     parsers[typeof(long)] = StructParserExtensions.LongToObjParse;   
     return parsers; 
    }   
} 

Я предположил, что использование делегатов резко повысить производительность, так что будет близко к прямому вызову, но я был неправ, что до сих пор примерно в 100 раз медленнее (на 100 reties) Here is the fiddle

Мои вопросы:

  1. В то время как я видел несколько примеров, когда преобразование вызовов отражения в вызовы делегатов делает разницу, в этом случае это не делает работу. Зачем?
  2. Есть ли способ улучшить производительность в этом случае?
+0

Если вы сравниваете производительность этого кода с другим кодом, было бы полезно иметь ссылку на другой код. Его очень сложно сказать «Почему это происходит медленнее», когда мы не знаем, что такое более быстрый код. Существует несколько причин разницы в производительности. Я смотрю на ваш код и думаю: «Он использует отражение, конечно, его медленнее, чем версия, не использующая отражение». – Chris

+0

У меня есть ссылки на обе прикрепленные скрипты делегаты: https://dotnetfiddle.net/RYx0Zt отражение: https://dotnetfiddle.net/LcVqMy –

ответ

5

Вы только тестирование 100 итераций. В основном вы тестируете однократные накладные расходы на запуск. Увеличьте счетчик итераций до тех пор, пока каждый тест не займет 1 секунду. Таким образом, накладные расходы исчезают в шуме.

В данный момент ваш код рассчитан на 0,5 миллисекунды. Это далеко в диапазон шума. После фиксации, что я получаю:

00:00:00.9711365 
00:00:01.0958751 //Slightly slower 

Этот тест использует 1e7 итераций в то время как предыдущая, используемые 1E2. Также убедитесь, что вы тестируете в режиме деблокирования без отладки, прикрепленной к битне, о которой вы заботитесь.

+0

Прежде всего, спасибо за ваш ответ. Увеличивая количество итераций до 1e7, доказал свою точку зрения. Также похоже, что вызов отражения примерно в 10 раз медленнее, чем прямой вызов. Поэтому я вижу разницу. Интересным здесь является то, что если ожидаемое число итераций относительно невелико, чем разница между вызовом отражения и вызовом делегата относительно незначительна. –

+0

Вы не можете заключить, что, поскольку вы не измеряете только стили вызовов, а также одноразовые служебные данные. Отражение может иметь высокую стоимость запуска; Сначала вы можете попробовать запустить одну прогрессивную итерацию. Это отнимает одно время накладные расходы, и вы можете уйти с меньшим количеством итераций. – usr

2

Что случилось с:

 private object Converter(object inVal, Type t) 
    { 
     return Convert.ChangeType(inVal, t); 
    } 
+1

Что случилось с попыткой чего-то научиться чему-то в процессах? – Stralos

+1

@Stralos: Хотя он не отвечает первому вопросу, он, похоже, отвечает на второй вопрос о том, как иметь более эффективную версию кода. – Chris

0

Как указано ранее. При 100 итерациях вы, скорее всего, измеряете только накладные расходы.

Я провел тестирование немного дальше. Я присоединился к вашим кодам в один и провел 450 итераций, чтобы получить статистические данные. я установил, что для разбора 10 Milion раз (10^7)

Вот код: http://pastebin.com/65dhdX9t

Вот это результат нескольких последних итераций:
м-метод, д-делегат, г-отражения Statistics Подводя итог:
- Делегирование - пример. 1.195x медленнее против прямого вызова
- Отражение приближается. 6.105x медленнее против прямого звонка

Надеюсь, это поможет! Это, безусловно, помогло мне убедить меня перейти от размышления к делегации.

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