What is the difference between calling a delegate directly, using DynamicInvoke, and using DynamicInvokeImpl?

后端 未结 3 756
心在旅途
心在旅途 2020-12-14 01:36

The docs for both DynamicInvoke and DynamicInvokeImpl say:

Dynamically invokes (late-bound) the method represented by the current delegate.

3条回答
  •  無奈伤痛
    2020-12-14 02:14

    The main difference between calling it directly (which is short-hand for Invoke(...)) and using DynamicInvoke is performance; a factor of more than *700 by my measure (below).

    With the direct/Invoke approach, the arguments are already pre-validated via the method signature, and the code already exists to pass those into the method directly (I would say "as IL", but I seem to recall that the runtime provides this directly, without any IL). With DynamicInvoke it needs to check them from the array via reflection (i.e. are they all appropriate for this call; do they need unboxing, etc); this is slow (if you are using it in a tight loop), and should be avoided where possible.

    Example; results first (I increased the LOOP count from the previous edit, to give a sensible comparison):

    Direct: 53ms
    Invoke: 53ms
    DynamicInvoke (re-use args): 37728ms
    DynamicInvoke (per-cal args): 39911ms
    

    With code:

    static void DoesNothing(int a, string b, float? c) { }
    static void Main() {
        Action method = DoesNothing;
    
        int a = 23;
        string b = "abc";
        float? c = null;
        const int LOOP = 5000000;
    
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++) {
            method(a, b, c);
        }
        watch.Stop();
        Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms");
    
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++) {
            method.Invoke(a, b, c);
        }
        watch.Stop();
        Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms");
    
        object[] args = new object[] { a, b, c };
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++) {
            method.DynamicInvoke(args);
        }
        watch.Stop();
        Console.WriteLine("DynamicInvoke (re-use args): "
             + watch.ElapsedMilliseconds + "ms");
    
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++) {
            method.DynamicInvoke(a,b,c);
        }
        watch.Stop();
        Console.WriteLine("DynamicInvoke (per-cal args): "
             + watch.ElapsedMilliseconds + "ms");
    }
    

提交回复
热议问题