I have the following piece of code in a base-class:
public static void InvokeExternal(Delegate d, object param, object sender)
{
if (d != null)
{
//Check each invocation target
foreach (Delegate dDelgate in d.GetInvocationList())
{
if (dDelgate.Target != null && dDelgate.Target is System.ComponentModel.ISynchronizeInvoke
&& ((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).InvokeRequired)
{
//If target is ISynchronizeInvoke and Invoke is required, invoke via ISynchronizeInvoke
((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).Invoke(dDelgate, new object[] { sender, param });
}
else
{
//Else invoke dynamically
dDelgate.DynamicInvoke(sender, param);
}
}
}
}
This code sample is responsible for invoking an event, represented as multicast delegate, where the invocation targets include small classes which do not care about cross-threading, but also classes which implement ISynchronizeInvoke
and care a lot about cross-threading, like Windows Forms Controls.
In theory, this snippet works pretty fine, no errors occur. But the DynamicInvoke
is incredibly slow, not to say it's the current bottleneck of the application.
So, there goes my question: Is there any way to speed up this little function without breaking the functionally to subscribe to the event directly?
The signature of all events/delegates is (object sender, EventArgs param)
If dDelegate is a known type (ie Action) you could always cast to it and call it directly.
With that said if you are on .NET3.5 you can use Expression trees to get a fair bit of optimization. My example uses the concurrent dictionary in .NET4 but that's replacable with a normal dictionary and a lock.
The idea is as following: The delegate holds which method it's calling to. For each unique method that is called I create (using Expression trees) a compiled delegate that calls that specific method. Creating a compiled delegate is expensive that's why it's important to cache it but once created the compiled delegate is as fast as a normal delegate.
On my machine 3,000,000 calls took 1 sec with the compiled delegate and 16 sec with DynamicInvoke.
Edit: As OP uses .NET2 I added an example that should be compatible with .NET2 runtime (as I use VS2010 I might use some new language features by mistake but I did compile using .NET2 runtime).