Difference Between Invoke and DynamicInvoke
When you have a delegate instance, you might know the exact type, or you might just know that it is a Delegate
. If you know the exact type, you can use Invoke
, which is very fast - everything is already pre-validated. For example:
Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);
However! If you just know that it is Delegate
, it has to resolve the parameters etc manually - this might involve unboxing, etc - a lot of reflection is going on. For example:
Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);
Note I've written the args
long hand to make it clear that an object[]
is involved. There are lots of extra costs here:
- the array
- validating the passed arguments are a "fit" for the actual
MethodInfo
- unboxing etc as necessary
- reflection-invoke
- then the caller needs to do something to process the return value
Basically, avoid DynamicInvoke
when-ever you can. Invoke
is always preferable, unless all you have is a Delegate
and an object[]
.
For a performance comparison, the following in release mode outside of the debugger (a console exe) prints:
Invoke: 19ms
DynamicInvoke: 3813ms
Code:
Func<int,int> twice = x => x * 2;
const int LOOP = 5000000; // 5M
var watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
twice.Invoke(3);
}
watch.Stop();
Console.WriteLine("Invoke: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
twice.DynamicInvoke(3);
}
watch.Stop();
Console.WriteLine("DynamicInvoke: {0}ms", watch.ElapsedMilliseconds);
Delegate DynamicInvoke vs MethodInfo invoke?
Curiosity did get the better of me so I ended up benchmarking it myself. I still recommend doing it yourself to give it a go and see if you get the same results. If you had a specific situation in mind that might yield different results.
Results
I was kind of surprised at first, but here they are. I added in a standard Delegate.Invoke
just to compare.
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
MethodInfoInvoke | 116.9913 ns | 1.0848 ns | 1.0148 ns | 0.0024 | - | - | 40 B |
DelegateDynamicInvoke | 391.8141 ns | 6.6877 ns | 6.2557 ns | 0.0105 | - | - | 176 B |
DelegateInvoke | 0.4334 ns | 0.0037 ns | 0.0033 ns | - | - | - | - |
What is the difference between [Delegate].DynamicInvoke and [Delegate].Method.Invoke?
DynamicInvoke isn't performed asynchronously, it's dynamic because your parameters that you pass it to the function checking (and unboxing) in the runtime.Invoke requires exact type of parameters but DynamicInvoke requires an object array which contains your parameters. So you can use DynamicInvoke when you don't know exactly type of the parameter.
Here is full (and better :) explanation about it: Difference Between Invoke and DynamicInvoke
Calling Delegate.DynamicInvoke vs Func object ()
The performance difference is caused by the different performance of Invoke()
(fast) and DynamicInvoke()
(slow). When taking a look at the generated IL of a direct call to a Func<object>
typed delegate you can see that the resulting IL will actually call the Invoke()
method:
static void TestInvoke(Func<object> func) {
func();
}
The above compiles to IL code looking something like this (in a debug build):
.method private hidebysig static void TestInvoke(class [mscorlib]System.Func`1<object> func) cil managed {
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0 // func
IL_0002: callvirt instance !0/*object*/ class [mscorlib]System.Func`1<object>::Invoke()
IL_0007: pop
IL_0008: ret
} // end of method Program::TestInvoke
And the Invoke()
method is much faster than the DynamicInvoke()
method, since it basically doesn't need to resolve the type of the delegate (as it is already known). The following answer to another question explains the difference of Invoke()
and DynamicInvoke()
in a bit more detail:
https://stackoverflow.com/a/12858434/6122062
The following very simplified and probably not very accurate test shows a huge difference in perfomance. As you can see there I'm even using the same delegate, just calling it in different ways:
class Program {
static void Main(string[] args) {
var ex = Expression.Lambda<Func<object>>(Expression.New(typeof(object))).Compile();
Stopwatch timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestInvoke(ex);
Console.WriteLine($"Invoke():\t\t{timer.Elapsed.ToString()}");
timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestDynamicInvoke(ex);
Console.WriteLine($"DynamicInvoke():\t{timer.Elapsed.ToString()}");
Console.ReadKey(true);
}
static void TestInvoke(Func<object> func) {
func();
}
static void TestDynamicInvoke(Delegate deleg) {
deleg.DynamicInvoke();
}
}
Results on my PC at home using a release build, without an attached debugger (as mentioned above I know this simple test might not be very accuarate, but it demonstates the huge difference in performance)
Invoke(): 00:00:00.0080935
DynamicInvoke(): 00:00:00.8382236
Strange behavior with DynamicInvoke and Invoke
First thing to note: when looking at performance always ensure you are running a release/optimized build, and run it without the IDE attached. You can usually get away with ctrl+f5 in Visual Studio, but if in doubt: run it from the command line.
When 'a' is empty method and the loop runs 1000000 times the DynamicInvoke took approximately 4000 ms and the direct Invoke took 20 ms
Fair enough; I get 2729ms for DynamicInvoke
and 8ms for direct invoke; but that is what we expect; we know that DynamicInvoke
is slower. But everything here is as we expect. In fact, if we correct for the fact that Invoke
is unfairly running an extra level of delegate invoke (which we should expect to roughly-double the time taken), we get:
static void Main()
{
int x = 100;
double y = 1.2345678;
var objArgs = new object[] { x, y };
Action<int, double> a = (i, d) => { };
var sw = new Stopwatch();
sw.Start();
const int loop = 1000000;
for (int i = 0; i < loop; i++)
a.DynamicInvoke(objArgs);
Console.WriteLine("Dynamic: " + sw.ElapsedMilliseconds);
sw.Stop();
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < loop; i++)
a(x,y);
Console.WriteLine("Invoke: " + sw.ElapsedMilliseconds);
sw.Stop();
}
with results:
Dynamic: 2785
Invoke: 3
So far, nothing unexpected.
When i'm put in a Thread.Sleep(20) and the loop runs 1000 times then the DynamicInvoke and the direct Invoke took approximately 20 seconds
Yep, 20ms x 1000 is definitely 20 seconds. The 20ms sleep time is not exact; we expect some variance around that number, which adds up when multiplied by 1000. In particular, the sleep interrupt isn't going to fire for every single CPU cycle, so we should probably expect the slightly irregular sleep cycle to eat up the difference between a Invoke
and DynamicInvoke
(about 2.75 microseconds per call).
Changing loop
to 1000 and adding a Thread.Sleep(20)
I get:
Dynamic: 20395
Invoke: 20370
Again, nothing surprising here. In fact, with the slight randomness introduced by a Thread.Sleep()
, multiplied by 1000 I would find it perfectly acceptable if Invoke
occasionally took longer than Dynamic
.
I see nothing in the least bit surprising in the numbers.
What's the difference between Invoke() and BeginInvoke()
Do you mean Delegate.Invoke
/BeginInvoke
or Control.Invoke
/BeginInvoke
?
Delegate.Invoke
: Executes synchronously, on the same thread.Delegate.BeginInvoke
: Executes asynchronously, on athreadpool
thread.Control.Invoke
: Executes on the UI thread, but calling thread waits for completion before continuing.Control.BeginInvoke
: Executes on the UI thread, and calling thread doesn't wait for completion.
Tim's answer mentions when you might want to use BeginInvoke
- although it was mostly geared towards Delegate.BeginInvoke
, I suspect.
For Windows Forms apps, I would suggest that you should usually use BeginInvoke
. That way you don't need to worry about deadlock, for example - but you need to understand that the UI may not have been updated by the time you next look at it! In particular, you shouldn't modify data which the UI thread might be about to use for display purposes. For example, if you have a Person
with FirstName
and LastName
properties, and you did:
person.FirstName = "Kevin"; // person is a shared reference
person.LastName = "Spacey";
control.BeginInvoke(UpdateName);
person.FirstName = "Keyser";
person.LastName = "Soze";
Then the UI may well end up displaying "Keyser Spacey". (There's an outside chance it could display "Kevin Soze" but only through the weirdness of the memory model.)
Unless you have this sort of issue, however, Control.BeginInvoke
is easier to get right, and will avoid your background thread from having to wait for no good reason. Note that the Windows Forms team has guaranteed that you can use Control.BeginInvoke
in a "fire and forget" manner - i.e. without ever calling EndInvoke
. This is not true of async calls in general: normally every BeginXXX should have a corresponding EndXXX call, usually in the callback.
Can Delegate.DynamicInvoke be avoided in this generic code?
I strongly suspect that wrapping the calls would be a lot more efficient than using DynamicInvoke
. Your code would then be:
internal sealed class TypeDispatchProcessor
{
private readonly Dictionary<Type, Action<object>> _actionByType
= new Dictionary<Type, Action<object>>();
public void RegisterProcedure<T>(Action<T> action)
{
_actionByType[typeof(T)] = item => action((T) item);
}
public void ProcessItem(object item)
{
Action<object> action;
if (_actionByType.TryGetValue(item.GetType(), out action))
{
action(item);
}
}
}
It's worth benchmarking it, but I think you'll find this a lot more efficient. DynamicInvoke
has to check all the arguments with reflection etc, instead of the simple cast in the wrapped delegate.
Related Topics
Oledbparameters and Parameter Names
How to Get Current User Timezone in C#
Oraclecommand SQL Parameters Binding
Why Interface Layer/Abstract Classes Required in Our Project
How to Fix a .Net Windows Application Crashing at Startup with Exception Code: 0Xe0434352
How to Set Properties on Struct Instances Using Reflection
An ASP.NET Setting Has Been Detected That Does Not Apply in Integrated Managed Pipeline Mode
Merge Two (Or More) Lists into One, in C# .Net
Parallel.Foreach Slower Than Foreach
Dynamically Access Table in Ef Core 2.0
Dictionary Returning a Default Value If the Key Does Not Exist
Entity Framework Very Slow to Load for First Time After Every Compilation