Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么编译委托比声明委托快?_C#_.net_Performance_Delegates_Linq Expressions - Fatal编程技术网

C# 为什么编译委托比声明委托快?

C# 为什么编译委托比声明委托快?,c#,.net,performance,delegates,linq-expressions,C#,.net,Performance,Delegates,Linq Expressions,首先,这与它不同,令人惊讶的是,它正好相反。此外,我在研究这个问题时发现的所有链接和问题都源自2010-2012年的时间段,因此我决定在这里提出一个新问题,看看是否有一些关于.NET生态系统中代理行为的当前状态的讨论 这就是说,我使用的是.NET Core 2.0和.NET 4.7.1,我看到了一些奇怪的性能指标,这些指标与从编译表达式创建的委托以及描述和声明为CLR对象的委托有关 为了了解我是如何偶然发现这个问题的,我做了一个测试,测试中选择了1000和10000个对象的数组中的数据,并注意到

首先,这与它不同,令人惊讶的是,它正好相反。此外,我在研究这个问题时发现的所有链接和问题都源自2010-2012年的时间段,因此我决定在这里提出一个新问题,看看是否有一些关于.NET生态系统中代理行为的当前状态的讨论

这就是说,我使用的是.NET Core 2.0和.NET 4.7.1,我看到了一些奇怪的性能指标,这些指标与从编译表达式创建的委托以及描述和声明为CLR对象的委托有关

为了了解我是如何偶然发现这个问题的,我做了一个测试,测试中选择了1000和10000个对象的数组中的数据,并注意到如果我使用编译后的表达式,则会得到更快的结果。我设法将其归结为一个非常简单的项目,它再现了这个问题,您可以在这里找到:

对于测试,我使用了两组基准测试,它们的特点是一个已编译的委托与一个已声明的委托配对,结果总共有四个核心基准测试

第一个委托集由返回空字符串的空委托组成。第二个集合是一个委托,其中包含一个简单表达式。我想证明,这个问题发生在最简单的代理以及其中包含已定义主体的代理中

然后,通过卓越性能产品在CLR运行时和.NET核心运行时上运行这些测试,总共生成八个基准测试。此外,我还使用just as Perfective Benchmark.NET发出在基准测量JIT期间遇到的反汇编。我在下面分享这一结果

下面是运行基准测试的代码。你可以看到这是非常直截了当的:

[CoreJob, ClrJob, DisassemblyDiagnoser(true, printSource: true)]
public class Delegates
{
    readonly DelegatePair<string, string> _empty;
    readonly DelegatePair<string, int>    _expression;
    readonly string                       _message;

    public Delegates() : this(new DelegatePair<string, string>(_ => default, _ => default),
                              new DelegatePair<string, int>(x => x.Length, x => x.Length)) {}

    public Delegates(DelegatePair<string, string> empty, DelegatePair<string, int> expression,
                     string message = "Hello World!")
    {
        _empty      = empty;
        _expression = expression;
        _message    = message;
        EmptyDeclared();
        EmptyCompiled();
        ExpressionDeclared();
        ExpressionCompiled();
    }

    [Benchmark]
    public void EmptyDeclared() => _empty.Declared(default);

    [Benchmark]
    public void EmptyCompiled() => _empty.Compiled(default);

    [Benchmark]
    public void ExpressionDeclared() => _expression.Declared(_message);

    [Benchmark]
    public void ExpressionCompiled() => _expression.Compiled(_message);
}
请注意,使用已编译委托的基准测试始终更快

最后,以下是每个基准所遇到的反汇编结果:


表{边框折叠:折叠;显示:块;宽度:100%;溢出:自动;}
td,th{padding:6px 13px;border:1px solid#ddd;}
tr{背景色:#fff;边框顶部:1px实心#ccc;}
tr:n个孩子(偶数){背景:#f8f8;}
代理。已声明为空
.NET Framework 4.7.1(CLR 4.0.30319.42000),64位RyuJIT-v4.7.2633.0
.NET Core 2.0.7(CoreCLR 4.6.26328.01,CoreFX 4.6.26403.03),64位RyuJIT
Delegates.EmptyCompiled
.NET Framework 4.7.1(CLR 4.0.30319.42000),64位RyuJIT-v4.7.2633.0
.NET Core 2.0.7(CoreCLR 4.6.26328.01,CoreFX 4.6.26403.03),64位RyuJIT
degregates.expression声明
.NET Framework 4.7.1(CLR 4.0.30319.42000),64位RyuJIT-v4.7.2633.0
.NET Core 2.0.7(CoreCLR 4.6.26328.01,CoreFX 4.6.26403.03),64位RyuJIT
Delegates.ExpressionCompiled
.NET Framework 4.7.1(CLR 4.0.30319.42000),64位RyuJIT-v4.7.2633.0
.NET Core 2.0.7(CoreCLR 4.6.26328.01,CoreFX 4.6.26403.03),64位RyuJIT
我是不是在做一些完全离谱的事情?(猜这应该是第一个问题。:)

我可以合理地确定,您看到的反汇编只针对基准方法:加载委托及其参数,然后调用委托所需的指令。它不包括每个代理的主体

这就是为什么唯一的区别是其中一条
mov
指令中的相对偏移量:一个委托位于结构中的偏移量0处,另一个位于偏移量8处。交换
已编译的
已声明的
的声明顺序,并查看反汇编是如何更改的

我不知道有什么方法可以让Benchmark.NET为调用树中更深层次的调用进行反汇编。文档建议将
[DisassemblyDiagnoser]
上的
recursiveDepth
设置为某个值
n>1
,但在这种情况下似乎不起作用


你是说我们没有看到额外的拆卸吗

正确,您没有看到代理实体的反汇编。如果它们在编译方式上存在差异,那么就可以看到它们

你是说我们没有看到额外的拆卸吗?由于这两个机构是完全相同的(或至少看起来是相同的),我进一步不清楚情况会如何

身体不一定相同。对于基于
表达式的lambas,C#编译器不会为所描述的表达式发出IL;相反,它发出一系列
Expression
factory调用,以在运行时构建表达式树。该表达式树描述的代码在功能上应该与生成它的C表达式等效,但它是由运行时调用
Compile()
时由
LambdaCompiler
编译的。LINQ表达式树意味着与语言无关,不一定与C#编译器生成的表达式具有精确的奇偶性。由于lambda表达式是由不同(且不太复杂)的编译器编译的,因此生成的IL可能与C#编译器发出的有点不同。例如,lambda编译器倾向于比C#编译器发出更多的临时局部变量,或者至少在我上次查看源代码时是这样

确定每个委托的实际反汇编的最佳方法可能是在调试器中加载。我自己也试过这么做,但我似乎不知道如何让它在VS2017中工作。我过去从来没有遇到过麻烦。我还没有完全适应VS2017中的新项目模型,也不知道如何启用非托管调试


好的,我用WinDbg加载了SOS.dll,在谷歌搜索了一下之后,我现在可以查看IL和反汇编了。首先,让我们看一下方法
BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.371 (1709/FallCreatorsUpdate/Redstone3)
Intel Core i7-4820K CPU 3.70GHz (Haswell), 1 CPU, 8 logical and 8 physical cores
.NET Core SDK=2.1.300-preview2-008533
  [Host] : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT
  Clr    : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2633.0
  Core   : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT


             Method |  Job | Runtime |      Mean |     Error |    StdDev |
------------------- |----- |-------- |----------:|----------:|----------:|
      EmptyDeclared |  Clr |     Clr | 1.3691 ns | 0.0302 ns | 0.0282 ns |
      EmptyCompiled |  Clr |     Clr | 1.1851 ns | 0.0381 ns | 0.0357 ns |
 ExpressionDeclared |  Clr |     Clr | 1.3805 ns | 0.0314 ns | 0.0294 ns |
 ExpressionCompiled |  Clr |     Clr | 1.1431 ns | 0.0396 ns | 0.0371 ns |
      EmptyDeclared | Core |    Core | 1.5733 ns | 0.0329 ns | 0.0308 ns |
      EmptyCompiled | Core |    Core | 0.9326 ns | 0.0275 ns | 0.0244 ns |
 ExpressionDeclared | Core |    Core | 1.6040 ns | 0.0394 ns | 0.0368 ns |
 ExpressionCompiled | Core |    Core | 0.9380 ns | 0.0485 ns | 0.0631 ns |
0:000> !DumpMD 000007fe97686148

Method Name:  StackOverflow.Performance.Delegates.Delegates+<>c.<.ctor>b__3_2(System.String)
Class:        000007fe977d14d0
MethodTable:  000007fe97686158
mdToken:      000000000600000e
Module:       000007fe976840c0
IsJitted:     yes
CodeAddr:     000007fe977912b0
Transparency: Critical
0:000> !DumpMD 000007fe97689390

Method Name:  DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure, System.String)
Class:        000007fe97689270
MethodTable:  000007fe976892e8
mdToken:      0000000006000000
Module:       000007fe97688af8
IsJitted:     yes
CodeAddr:     000007fe977e0150
Transparency: Transparent
0:000> !DumpIL 000007fe97686148

IL_0000: ldarg.1 
IL_0001: callvirt 6000002 System.String.get_Length()
IL_0006: ret 

0:000> !DumpIL 000007fe97689390

IL_0000: ldarg.1 
IL_0001: callvirt System.String::get_Length 
IL_0006: ret
0:000> !U 000007fe977912b0

Normal JIT generated code
StackOverflow.Performance.Delegates.Delegates+<>c.<.ctor>b__3_2(System.String)
Begin 000007fe977912b0, size 4
W:\dump\DelegateBenchmark\StackOverflow.Performance.Delegates\Delegates.cs @ 14:

000007fe`977912b0 8b4208          mov     eax,dword ptr [rdx+8]
000007fe`977912b3 c3              ret

0:000> !U 000007fe977e0150

Normal JIT generated code
DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure, System.String)
Begin 000007fe977e0150, size 4

000007fe`977e0150 8b4208          mov     eax,dword ptr [rdx+8]
000007fe`977e0153 c3              ret
0:000> !DumpVC /d 000007fe97686040 0000000002a84410

Name:        StackOverflow.Performance.Delegates.DelegatePair`2[[System.String, mscorlib],[System.Int32, mscorlib]]
MethodTable: 000007fe97686040
EEClass:     000007fe977d12d0
Size:        32(0x20) bytes
File:        W:\dump\DelegateBenchmark\StackOverflow.Performance.Delegates\bin\Release\net461\StackOverflow.Performance.Delegates.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef692e400  4000001        0 ...Int32, mscorlib]]  0 instance 0000000002a8b4d8 <Declared>k__BackingField
000007fef692e400  4000002        8 ...Int32, mscorlib]]  0 instance 0000000002a8d3f8 <Compiled>k__BackingField
0:000> !U 7fe977e0150 

Normal JIT generated code
DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure, System.String)
Begin 000007fe977e0150, size 4

000007fe`977e0150 8b4208          mov     eax,dword ptr [rdx+8]
000007fe`977e0153 c3              ret
0:000> !U 7fe977901d8 

Unmanaged code

000007fe`977901d8 e8f326635f      call    clr!PrecodeFixupThunk (000007fe`f6dc28d0)
000007fe`977901dd 5e              pop     rsi
000007fe`977901de 0400            add     al,0
000007fe`977901e0 286168          sub     byte ptr [rcx+68h],ah
000007fe`977901e3 97              xchg    eax,edi
000007fe`977901e4 fe07            inc     byte ptr [rdi]
000007fe`977901e6 0000            add     byte ptr [rax],al
000007fe`977901e8 0000            add     byte ptr [rax],al
000007fe`977901ea 0000            add     byte ptr [rax],al
000007fe`977901ec 0000            add     byte ptr [rax],al