Search code examples
c#performancereflectiondelegatesdynamic-invoke

Strange behavior with DynamicInvoke and Invoke


I read this post and I tried to do the same but I came across a very strange behavior when comparing the del.DynamicINvoke(args) Vs. del(args)

Update

So after Jon and usr comments i post now new working code.

I'll really appreciate any help!

Code:

using System;
using System.Diagnostic;
using System.Threading;

namespace DynamicInvokeVsInvoke {
  public class Program {
    static void Main(string[] args) {
      var objArgs = new object[] {100, 1.2345678 };
      Action<int, double> a = (i, d) => {};
      Action<object[]> action = o => a((int)o[0], (double)o[1]);
      var sw = new Stopwatch();
      sw.Start();
      for (int i = 0; i < 1000; i++)
         a.DynamicInvoke(objArgs);
      Console.WriteLine("Dynamic: " + sw.ElapsedMilliseconds);
      sw.Stop();
      sw = new Stopwatch();
      sw.Start();
      for (int i = 0; i < 1000; i++)
         action(objArgs);
      Console.WriteLine("Invoke: " + sw.ElapsedMilliseconds);
      sw.Stop();
    }
  }
}

Results:

When 'a' is empty method and the loop runs 1000000 times the DynamicInvoke took approximately 4000 ms and the direct Invoke took 20 ms

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

p.s. i can't copy/paste from vs for some reason so i write the code manually if you see syntax error please let me know


Solution

  • 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.