Моей цели состоит в написании слабо типизированного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
Мои вопросы:
- В то время как я видел несколько примеров, когда преобразование вызовов отражения в вызовы делегатов делает разницу, в этом случае это не делает работу. Зачем?
- Есть ли способ улучшить производительность в этом случае?
Если вы сравниваете производительность этого кода с другим кодом, было бы полезно иметь ссылку на другой код. Его очень сложно сказать «Почему это происходит медленнее», когда мы не знаем, что такое более быстрый код. Существует несколько причин разницы в производительности. Я смотрю на ваш код и думаю: «Он использует отражение, конечно, его медленнее, чем версия, не использующая отражение». – Chris
У меня есть ссылки на обе прикрепленные скрипты делегаты: https://dotnetfiddle.net/RYx0Zt отражение: https://dotnetfiddle.net/LcVqMy –