class Program
{
static Dictionary<int, List<String>> _functions =
new Dictionary<int, List<string>>();
static Dictionary<int, List<MethodInfo>> _methods =
new Dictionary<int, List<MethodInfo>>();
static Dictionary<int, List<MethodBase>> _methodBases =
new Dictionary<int, List<MethodBase>>();
static Dictionary<int, List<Delegate>> _delegate =
new Dictionary<int, List<Delegate>>();
static Stopwatch _stopwatch = new Stopwatch();
static void Main(string[] args)
{
Console.WriteLine("Frequency = " + Stopwatch.Frequency);
DyanmicInvocationTest();
Console.WriteLine("Press any key to exit!");
Console.ReadKey();
}
static void DyanmicInvocationTest()
{
Functions fn = new Functions();
Type typeFn = fn.GetType();
MethodInfo[] methods = typeFn.GetMethods();
foreach (MethodInfo method in methods)
{
if (method.Name.StartsWith("GP"))
{
ParameterInfo[] pi = method.GetParameters();
if (!_functions.ContainsKey(pi.Length))
{
_functions.Add(pi.Length, new List<string>());
}
_functions[pi.Length].Add(method.Name);
if (!_methods.ContainsKey(pi.Length))
{
_methods.Add(pi.Length, new List<MethodInfo>());
}
_methods[pi.Length].Add(method);
if (!_methodBases.ContainsKey(pi.Length))
{
_methodBases.Add(pi.Length, new List<MethodBase>());
}
_methodBases[pi.Length].Add(method);
if (!_delegate.ContainsKey(pi.Length))
{
_delegate.Add(pi.Length, new List<Delegate>());
}
MemberInfo[] members = typeFn.GetMembers();
switch(pi.Length)
{
case 0:
_delegate[pi.Length].Add(
Delegate.CreateDelegate(
typeof(Functions.ZeroParam), fn, method));
break;
case 1:
_delegate[pi.Length].Add(
Delegate.CreateDelegate(
typeof(Functions.OneParam), fn, method));
break;
default:
break;
}
}
}
int numRuns = 100 * 1000;
long time = 0;
// Member
Console.WriteLine("*** Running dynamic member invoke test ***");
for (int i = 0; i < numRuns; ++i)
{
time += DynamicInvokeMember(fn);
_stopwatch.Reset();
}
Console.WriteLine("Avg dynamic member invoke = " + (double)time/(double)numRuns);
time = 0;
// Method
Console.WriteLine("*** Running dynamic method invoke test ***");
for (int i = 0; i < numRuns; ++i)
{
time += DynamicInvokeMethod(fn);
_stopwatch.Reset();
}
Console.WriteLine("Avg dynamic method invoke = " + (double)time / (double)numRuns);
time = 0;
// Method Base
Console.WriteLine("*** Running dynamic method base invoke test ***");
for (int i = 0; i < numRuns; ++i)
{
time += DynamicInvokeMethodBase(fn);
_stopwatch.Reset();
}
Console.WriteLine("Avg dynamic method base invoke = " + (double)time / (double)numRuns);
time = 0;
// Delegate
Console.WriteLine("*** Running dynamic delegate test ***");
for (int i = 0; i < numRuns; ++i)
{
time += DynamicInvokeDelegate(fn);
_stopwatch.Reset();
}
Console.WriteLine("Avg dynamic delegate invoke = " + (double)time / (double)numRuns);
time = 0;
// Normal
Console.WriteLine("*** Running normal invocation test ***");
for (int i = 0; i < numRuns; ++i)
{
time += NormalInvoke(fn);
_stopwatch.Reset();
}
Console.WriteLine("Average time normal = " + (double)time / (double)numRuns);
}
static Int64 DynamicInvokeMember(Functions fn)
{
Type typeFn = fn.GetType();
object[] zeroParam = new object[0];
object[] oneParam = new object[1] { 1.0 };
_stopwatch.Start();
foreach (int key in _functions.Keys)
{
//Console.WriteLine(
// String.Format("num param {0}, num fn {1}",
// key, _functions[key].Count));
foreach (String function in _functions[key])
{
switch (key)
{
case 0:
typeFn.InvokeMember(function,
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
fn,
zeroParam,
null);
break;
case 1:
typeFn.InvokeMember(function,
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
fn,
oneParam,
null);
break;
default:
break;
}
}
}
_stopwatch.Stop();
return _stopwatch.ElapsedTicks;
}
static Int64 DynamicInvokeMethod(Functions fn)
{
Type typeFn = fn.GetType();
object[] zeroParam = new object[0];
object[] oneParam = new object[1]{1.0};
_stopwatch.Start();
foreach (int key in _methods.Keys)
{
//Console.WriteLine(
// String.Format("num param {0}, num fn {1}",
// key, _functions[key].Count));
foreach (MethodInfo method in _methods[key])
{
switch (key)
{
case 0:
method.Invoke(fn, zeroParam);
break;
case 1:
method.Invoke(fn, oneParam);
break;
default:
break;
}
}
}
_stopwatch.Stop();
return _stopwatch.ElapsedTicks;
}
static Int64 DynamicInvokeMethodBase(Functions fn)
{
Type typeFn = fn.GetType();
object[] zeroParam = new object[0];
object[] oneParam = new object[1] { 1.0 };
_stopwatch.Start();
foreach (int key in _methodBases.Keys)
{
//Console.WriteLine(
// String.Format("num param {0}, num fn {1}",
// key, _functions[key].Count));
foreach (MethodBase method in _methodBases[key])
{
switch (key)
{
case 0:
method.Invoke(fn, zeroParam);
break;
case 1:
method.Invoke(fn, oneParam);
break;
default:
break;
}
}
}
_stopwatch.Stop();
return _stopwatch.ElapsedTicks;
}
static Int64 DynamicInvokeDelegate(Functions fn)
{
Type typeFn = fn.GetType();
object[] zeroParam = new object[0];
object[] oneParam = new object[1] { 1.0 };
_stopwatch.Start();
foreach (int key in _delegate.Keys)
{
//Console.WriteLine(
// String.Format("num param {0}, num fn {1}",
// key, _functions[key].Count));
foreach (Delegate method in _delegate[key])
{
switch (key)
{
case 0:
method.DynamicInvoke(zeroParam);//.Invoke(fn, zeroParam);
break;
case 1:
method.DynamicInvoke(oneParam);
break;
default:
break;
}
}
}
_stopwatch.Stop();
return _stopwatch.ElapsedTicks;
}
static long NormalInvoke(Functions fn)
{
_stopwatch.Start();
fn.GPConstNumPI();
fn.GPConstNumZero();
fn.GPMACD(1.0);
fn.GPSin(1.0);
_stopwatch.Stop();
return _stopwatch.ElapsedTicks;
}
}
The DynamicInvokeMethod directly invokes the method, rather than looking it up by string from the Type, which offers a pretty large performance increase. There is no change to the NormalInvoke method.
So the results for the run with calculations are as follows:
- The normal method invocation took an average of 213 CPU Ticks.
- The dynamic method invocation took on average about 272 CPU Ticks.
- The dynamic member invocation took an average of 304 CPU Ticks.
The results for the run without calculations is:
- The normal method invocation took an average of 2.52 CPU Ticks.
- The dynamic method invocation took on average about 61.71 CPU Ticks.
- The dynamic member invocation took an average of 97.93 CPU Ticks.
I found some resources from other people and their performance analysis:
Simon Lucas at the University of Essex:
- Discusses similar results with reflection in Java.
- Link: http://algoval.essex.ac.uk/rep/oogp/ReflectionBasedGP.pdf
- According to the research in the paper reflection is about 100 times slower than normal method invocation and my results tend to support that finding.
Mattias Fagerlund's Coding Blog:
- Also talks about the performance of reflection.
- Link: http://lotsacode.wordpress.com/2010/04/13/reflection-type-getproperties-and-performance/
- Similar results and he discusses some caching techniques.
No comments:
Post a Comment