Reputation: 8781
My goal is to write a weakly typed TryParse
method that is going support basically all the available struct types (int, long, float...)
public static bool TryParse(Type type, string s, out object obj)
The implementation is going to invoke the TryParse
method of the provided type
argument (if type is int
, int.TryPase
will be invoked and the out value is going to be returned as object).
I've implemented it via reflection, but there is a major performance penalty (as I expected)
The Reflection Implementation:
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;
};
}
}
And here is the performance test:
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);
}
}
The average result is that reflection invocation is a bout 200 times slower than direct invocation (on 100 reties). Fiddle that demonstrates the reflection test
I tried to improve performance by using a cached delegates:
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;
}
}
I assumed that using delegates will drastically improve the performance so it will be close to the direct invocation, but I was wrong it is still about 100 times slower (on 100 reties) Here is the fiddle
My questions are:
Upvotes: 6
Views: 3179
Reputation: 667
As it was stated before. With 100 iterations you are most probably measuring only the overhead.
I took the testing a little bit further. I joined your codes into one and ran 450 iteration to get some statistical data. I set it to parse 10 milion times (10^7)
Here is the code: http://pastebin.com/65dhdX9t
Here is the result of few last iterations:
m-method, d-delegate, r-reflection
To sum it up:
- Delegation is aprox. 1.195x slower against direct call
- Reflection is aprox. 6.105x slower against direct call
Hope it helps! It sure helped me to convince me to transfer from reflection to delegation.
Upvotes: 1
Reputation: 356
What's wrong with:
private object Converter(object inVal, Type t)
{
return Convert.ChangeType(inVal, t);
}
Upvotes: 2
Reputation: 171178
You're just testing 100 iterations. You're mostly testing one-time startup overhead. Increase the iteration count until each test takes 1 second. That way the overhead disappears in the noise.
Currently, your code runs for .5 milliseconds. That is far in the noise range. After fixing that I get:
00:00:00.9711365
00:00:01.0958751 //Slightly slower
This benchmark uses 1e7 iterations whereas the previous one used 1e2. Also make sure to test in Release mode without debugger attached on the bitness that you care about.
Upvotes: 6