Difference Between Invoke and Dynamicinvoke

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.















































MethodMeanErrorStdDevGen 0Gen 1Gen 2Allocated
MethodInfoInvoke116.9913 ns1.0848 ns1.0148 ns0.0024--40 B
DelegateDynamicInvoke391.8141 ns6.6877 ns6.2557 ns0.0105--176 B
DelegateInvoke0.4334 ns0.0037 ns0.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 a threadpool 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



Leave a reply



Submit