为什么 CreateDelegate 比直接调用更快?

问题描述 投票:0回答:1

注意:我知道这段代码与roslyn不兼容

为什么 CreateDelegate 比直接调用更快?

任何人都可以解释为什么 Delegate.CreateDelegate 比直接调用委托更快吗?

(.NET 3.5,调试/发布版本类似..)

参见示例代码:

static void Main(string[] args)
{
        var myDelegate = new Func<string, string>((s) => s);


        // Direct Call
        Stopwatch sw = new Stopwatch();
        sw.Start();

        for (int i = 0; i < 10000000; i++)
        {
            myDelegate("test");
        }

        sw.Stop();

        Console.WriteLine("DirectCall: \t\t{0}ms" ,sw.ElapsedMilliseconds);


        sw.Reset();

        // CreateDeleagetCall

        var myDelegateCaller = (Func<string, string>)Delegate.CreateDelegate(typeof(Func<string, string>), myDelegate.Method);
        sw.Start();

        for (int i = 0; i < 10000000; i++)
        {
            myDelegateCaller("test");
        }

        sw.Stop();

        Console.WriteLine("CreateDelegateCall: \t{0}ms", sw.ElapsedMilliseconds);
        Console.ReadLine();
    }
c# .net c#-3.0
1个回答
0
投票

.Net core 8.0 和 BenchmarkDotNet - 相同的结果:

using System.Linq.Expressions;
using System.Reflection;
using BenchmarkDotNet.Attributes;

namespace SomeBenchmarks;

public class InstanceMethodInvoke
{
  readonly Foo _foo = new();
  Action<Foo> _exprAction;
  Action _delegateAction;

  [GlobalSetup]
  public void Setup()
  {
    var methodInfo = typeof(Foo).GetMethod("Bar", BindingFlags.Public | BindingFlags.Instance)
                  ?? throw new MissingMemberException(nameof(Foo), nameof(Foo.Bar));
    var instanceExpr = Expression.Parameter(typeof(Foo));
    _exprAction = Expression.Lambda<Action<Foo>>(Expression.Call(instanceExpr, methodInfo), instanceExpr).Compile();
    _delegateAction = (Action)Delegate.CreateDelegate(typeof(Action), _foo, methodInfo);
  }

  [Benchmark]
  public void InvokeFuncByExpression()
  {
    foreach (var _ in Enumerable.Repeat(1, 1000))
      _exprAction(_foo);
  }

  [Benchmark]
  public void InvokeFuncByDelegate()
  {
    foreach (var _ in Enumerable.Repeat(1, 1000))
      _delegateAction();
  }

  [Benchmark(Baseline = true)]
  public void DirectCall()
  {
    foreach (var _ in Enumerable.Repeat(1, 1000))
      _foo.Bar();
  }
}

public class Foo
{
  public void Bar(){}
}

基准测试结果:

// * Summary *

BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4291/22H2/2022Update)
Intel Core i7-10700F CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.100-preview.1.24101.2
  [Host]     : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2


| Method                 | Mean     | Error     | StdDev    | Ratio |
|----------------------- |---------:|----------:|----------:|------:|
| InvokeFuncByExpression | 1.312 μs | 0.0061 μs | 0.0057 μs |  1.12 |
| InvokeFuncByDelegate   | 1.104 μs | 0.0040 μs | 0.0037 μs |  0.94 |
| DirectCall             | 1.172 μs | 0.0051 μs | 0.0045 μs |  1.00 |
© www.soinside.com 2019 - 2024. All rights reserved.