Click here to Skip to main content
15,886,519 members
Articles / Programming Languages / C#

Benchmarking Direct, Delegate and Reflection Method Invocations

Rate me:
Please Sign up or sign in to vote.
4.61/5 (16 votes)
5 Jan 20032 min read 141.1K   446   30  
This console mode applet illustrates the significant performance hit of methods invoked using reflection.
using System;
using System.Reflection;
using System.Timers;

namespace CallBenchmark
{
	delegate void SNPCall();
	delegate void SPCall(int i, int j, int k);
	delegate void INPCall();
	delegate void IPCall(int i, int j, int k);

	class CallBenchmark
	{
		static int count;
		static bool done;

		public CallBenchmark()
		{
		}

		public static void StaticDirectCallWithoutParams()
		{
			++count;
		}

		public static void StaticDirectCallWithParams(int i, int j, int k)
		{
			++count;
		}

		public void InstanceDirectCallWithoutParams()
		{
			++count;
		}

		public void InstanceDirectCallWithParams(int i, int j, int k)
		{
			++count;
		}

		public static void StaticDelegateWithoutParams()
		{
			++count;
		}

		public static void StaticDelegateWithParams(int i, int j, int k)
		{
			++count;
		}

		public void InstanceDelegateWithoutParams()
		{
			++count;
		}

		public void InstanceDelegateWithParams(int i, int j, int k)
		{
			++count;
		}

		public static void StaticInvokeWithoutParams()
		{
			++count;
		}

		public static void StaticInvokeWithParams(int i, int j, int k)
		{
			++count;
		}

		public void InstanceInvokeWithoutParams()
		{
			++count;
		}

		public void InstanceInvokeWithParams(int i, int j, int k)
		{
			++count;
		}

		static void Main(string[] args)
		{
			CallBenchmark cb=new CallBenchmark();

			SNPCall snpCall=new SNPCall(StaticDelegateWithoutParams);
			SPCall spCall=new SPCall(StaticDelegateWithParams);
			INPCall inpCall=new INPCall(cb.InstanceDelegateWithoutParams);
			IPCall ipCall=new IPCall(cb.InstanceDelegateWithParams);

			MethodInfo snpMI=GetMethodInfo("CallBenchmark.exe/CallBenchmark.CallBenchmark/StaticInvokeWithoutParams");
			MethodInfo spMI=GetMethodInfo("CallBenchmark.exe/CallBenchmark.CallBenchmark/StaticInvokeWithParams");
			MethodInfo inpMI=GetMethodInfo("CallBenchmark.exe/CallBenchmark.CallBenchmark/InstanceInvokeWithoutParams");
			MethodInfo ipMI=GetMethodInfo("CallBenchmark.exe/CallBenchmark.CallBenchmark/InstanceInvokeWithParams");

			object[] parms=new Object[] {1, 2, 3};

			int sampleSize=20;					// # of samples
			int timerInterval=200;				// in ms
			int[] benchmarks=new int[12];
			string[] benchmarkName=new string[12]
			{
				"Static Direct Call Without Params",
				"Static Direct Call With Params",
				"Instance Direct Call Without Params",
				"Instance Direct Call With Params",

				"\nStatic Delegate Call Without Params",
				"Static Delegate Call With Params",
				"Instance Delegate Call Without Params",
				"Instance Delegate Call With Params",

				"\nStatic Reflection Invoke Call Without Params",
				"Static Reflection Invoke Call With Params",
				"Instance Reflection Invoke Call Without Params",
				"Instance Reflection Invoke Call With Params",
			};
			
			for (int i=0; i<benchmarks.Length; i++)
			{
				benchmarks[i]=0;
			}

			Timer timer=new Timer(timerInterval);
			timer.AutoReset=false;
			timer.Stop();
			timer.Elapsed+=new ElapsedEventHandler(OnTimerEvent);

			int q=sampleSize;

			while (q > 0)
			{
				Console.WriteLine("Pass # "+(sampleSize+1-q).ToString());

				// ***************** Direct Calls
				int n=0;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					StaticDirectCallWithoutParams();
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					StaticDirectCallWithParams(1, 2, 3);
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					cb.InstanceDirectCallWithoutParams();
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					cb.InstanceDirectCallWithParams(1, 2, 3);
				}
				benchmarks[n]+=count;

				// ***************** Delegate Calls
				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					snpCall();
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					spCall(1, 2, 3);
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					inpCall();
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					ipCall(1, 2, 3);
				}
				benchmarks[n]+=count;

				// ***************** Reflection Invoke Calls
				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					snpMI.Invoke(null, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static, null, null, null);
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					spMI.Invoke(null, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static, null, parms, null);
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					inpMI.Invoke(cb, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static, null, null, null);
				}
				benchmarks[n]+=count;

				++n;
				count=0;
				done=false;
				timer.Start();
				while (!done)
				{
					ipMI.Invoke(cb, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static, null, parms, null);
				}
				benchmarks[n]+=count;

				--q;
			}

			Console.WriteLine("Sample size = "+sampleSize.ToString());
			Console.WriteLine("Timer interval = "+timerInterval.ToString()+" ms");
			Console.WriteLine();
			for (int i=0; i<benchmarks.Length; i++)
			{
				benchmarks[i]/=(sampleSize-2);
				Console.WriteLine(benchmarkName[i]+": "+benchmarks[i].ToString());
			}
		}

		static void OnTimerEvent(object src, ElapsedEventArgs e)
		{
			done=true;
		}

		/// <summary>
		/// Returns a MethodInfo object with the parsed reflect string.
		/// </summary>
		/// <param name="reflection">A reflection method name, in the format "assembly/namespace.class/method".</param>
		static public MethodInfo GetMethodInfo(string reflection)
		{
			MethodInfo mi=null;
			string[] info=reflection.Split(new char[] {'/'});
			Assembly mainAssembly=Assembly.LoadFrom(info[0]);
			Type type=mainAssembly.GetType(info[1]);
			mi=type.GetMethod(info[2]);
			return mi;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions