Today, I come across Chris Brumme's blog on TransparentProxy, I really enjoy his blog, in particular those dedicated to the inner-working of CLR, one of the surprising sentence I read from his TransparentProxy article is that:
One surprising fact is that this is also why Delegate.BeginInvoke / EndInvoke are so slow compared to equivalent techniques like ThreadPool.QueueUserWorkItem (or UnsafeQueueUserWorkItem if you understand the security implications and want to be really efficient). The codepath for BeginInvoke / EndInvoke quickly turns into the common Message processing code of the general remoting pathway.
I have to say I get pretty stunned when reading this, so I write a little piece of code to verify his allegation:
using System;
using System.Threading;
using System.Diagnostics;
namespace DelegatePerformance
{
class Program
{
static void Main(string[] args)
{
ThreadStart threadStart = new ThreadStart(() => { });
WaitCallback waitCallback = new WaitCallback(a => { });
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = 0; i < 10000; i++)
{
threadStart.BeginInvoke(null, null);
}
stopWatch.Stop();
Console.WriteLine("Delegate.BeinInvoke(): {0}",stopWatch.ElapsedTicks.ToString());
stopWatch.Reset();
stopWatch.Start();
for (int i = 0; i < 10000; i++)
{
System.Threading.ThreadPool.QueueUserWorkItem(waitCallback);
}
stopWatch.Stop();
Console.WriteLine("ThreadPool.QueueUserWorkItem(): {0}", stopWatch.ElapsedTicks.ToString());
}
}
}
And the following output is what I get when running above programme:
Delegate.BeinInvoke(): 591441
ThreadPool.QueueUserWorkItem(): 91958
From the output, there is no brainer that Delegate.BeginInvoke is way much slower than the ThreadPool.QueueUserWorkItem call.
3 comments:
Great post. Thanks for actually profiling instead of solely using rhetorical devices, intuition, rules of thumb, hearsay, etc, to argue performance points, as is extremely popular on Internet forums related to programming.
I profiled QueueUserWorkItem vs. UnsafeQueueUserWorkItem and here are numbers:
ThreadPool.QueueUserWorkItem(): 45881667
ThreadPool.UnsafeQueueUserWorkItem(): 28368351
ThreadPool.QueueUserWorkItem(): 55213866
ThreadPool.UnsafeQueueUserWorkItem(): 31720158
ThreadPool.QueueUserWorkItem(): 47258991
ThreadPool.UnsafeQueueUserWorkItem(): 29558070
ThreadPool.QueueUserWorkItem(): 50587812
ThreadPool.UnsafeQueueUserWorkItem(): 29996415
ThreadPool.QueueUserWorkItem(): 57517047
ThreadPool.UnsafeQueueUserWorkItem(): 26509005
UnsafeQueueUserWorkItem is approximately twice as fast as QueueUserWorkItem, at a cost of call stack security.
Geez! That's 6.4 times faster... I'd better start using threadpooling. Thanks for the advise very useful A+++!
Nice ppost
Post a Comment