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#finally块会导致额外的方法调用?_C#_.net_Performance_Jit - Fatal编程技术网

为什么C#finally块会导致额外的方法调用?

为什么C#finally块会导致额外的方法调用?,c#,.net,performance,jit,C#,.net,Performance,Jit,我在创建一个应该尽可能快的小跟踪程序类时,正在查看生成的汇编代码。其思想是创建一个包装类,该类跟踪方法enter on leave,当该方法离开时跟踪方法leave trace。这可以通过使用语句来实现 public void DoStruct() { using var tmp = Wrapper.Create(nameof(DoStruct)); } 这将创建一个struct Tracer实例,该实例将在方法离开时自动释放。到现在为止,一直都还不错。现在让我们看看生成程序集: Ne

我在创建一个应该尽可能快的小跟踪程序类时,正在查看生成的汇编代码。其思想是创建一个包装类,该类跟踪方法enter on leave,当该方法离开时跟踪方法leave trace。这可以通过使用语句来实现

public void DoStruct()
{
    using var tmp = Wrapper.Create(nameof(DoStruct));
}
这将创建一个struct Tracer实例,该实例将在方法离开时自动释放。到现在为止,一直都还不错。现在让我们看看生成程序集:

NetCoreJitStruct.User.DoStruct()
Begin 00007FFC91651860, size 61
00007ffc`91651860 push    rbp
00007ffc`91651861 sub     rsp,30h
00007ffc`91651865 lea     rbp,[rsp+30h]
00007ffc`9165186a xor     eax,eax
00007ffc`9165186c mov     qword ptr [rbp-8],rax
00007ffc`91651870 mov     qword ptr [rbp-10h],rsp
00007ffc`91651874 mov     qword ptr [rbp+10h],rcx
00007ffc`91651878 mov rcx,268F07F30D8h
00007ffc`91651882 mov     rcx,qword ptr [rcx]
00007ffc`91651885 call    00007ffc`91650800 (NetCoreJitStruct.Wrapper.Create(System.String) *** Ctor called
00007ffc`9165188a mov     qword ptr [rbp-8],rax
00007ffc`9165188e jmp     00007ffc`91651890
00007ffc`91651890 mov     rcx,rsp
00007ffc`91651893 call    00007ffc`9165189f (NetCoreJitStruct.User.DoClass() *** Dispose Called via extra method Call!
00007ffc`91651898 nop
00007ffc`91651899 lea     rsp,[rbp]
00007ffc`9165189d pop     rbp
00007ffc`9165189e ret
    00007ffc`9165189f push    rbp  ** Dispose wrapper method
    00007ffc`916518a0 sub     rsp,30h
    00007ffc`916518a4 mov     rbp,qword ptr [rcx+20h]
    00007ffc`916518a8 mov     qword ptr [rsp+20h],rbp
    00007ffc`916518ad lea     rbp,[rbp+30h]
    00007ffc`916518b1 lea     rcx,[rbp-8]
    00007ffc`916518b5 call    00007ffc`91650818 (NetCoreJitStruct.Wrapper.Dispose()
    00007ffc`916518ba nop
    00007ffc`916518bb add     rsp,30h
    00007ffc`916518bf pop     rbp
    00007ffc`916518c0 ret
我不明白的是,为什么JIT编译器将dispose方法调用分解为一个额外的包装器方法。对于.NET4.8+一些更低效的代码,其行为是相同的。 我已经检查了struct方法内联是否存在问题,但对于已释放的类,行为是相同的

这是我使用.NET所能达到的最快速度,还是我错过了一些使它更快/更JIT友好的模式

编译器:C#7+ .NET:.NET 4.8或.NET Core 3.1

下面是完整的源代码

使用制度; 使用System.Collections.Generic; 使用系统诊断; 使用System.Runtime.CompilerServices

命名空间netcorejit结构 { 班级计划 {


您所看到的是分支的成本,如果您删除if并尝试使用,那么结构将更改代码并尝试使用您将在运行时看到巨大差异的类

您还可以交换if/else,您将得到与当前相反的结果

复制for,一个用于结构,一个用于类,您将得到另一种结果


澄清

for循环

尝试,然后编译,运行

        for (int i = 0; i < Runs; i++)
        {
            user.DoStruct();
        }
        for (int i = 0; i < Runs; i++)
        {
            user.DoClass();
        }
for(int i=0;i
然后尝试,编译,运行

        for (int i = 0; i < Runs; i++)
        {
            user.DoStruct();
        }
        for (int i = 0; i < Runs; i++)
        {
            user.DoClass();
        }
for(int i=0;i
你会得到不同的结果


我知道我的答案与标题不符,但我在回答文章的第一行。

你看到的是分支的成本,如果你删除if并尝试使用,结构然后更改代码并尝试使用类,你将在运行时看到巨大的差异

您还可以交换if/else,您将得到与当前相反的结果

复制for,一个用于结构,一个用于类,您将得到另一种结果


澄清

for循环

尝试,然后编译,运行

        for (int i = 0; i < Runs; i++)
        {
            user.DoStruct();
        }
        for (int i = 0; i < Runs; i++)
        {
            user.DoClass();
        }
for(int i=0;i
然后尝试,编译,运行

        for (int i = 0; i < Runs; i++)
        {
            user.DoStruct();
        }
        for (int i = 0; i < Runs; i++)
        {
            user.DoClass();
        }
for(int i=0;i
你会得到不同的结果


我知道我的答案与标题不符,但我回答的是文章的第一行。

你确定Dispose包装器方法会给你带来性能问题吗?这不是包装器方法,而是一个。有一件事应该优化简单的场景,但我不会假装理解这件事“我明白了。@罗伯特·哈维:通过计算超级指令:是的。你可以通过调用DostructNofInly来尝试,它会生成我所期望的代码。你是否对这段代码运行了探查器,并确定这是一个实际的性能问题,与你的一个非功能性软件需求相冲突?因为funclet对于正确的异常堆栈展开,只要需要
finally
,您现在看到的代码可能是JIT所能做的最好的代码。除非从事这项工作的人员之一能够挺身而出,找出
finally
克隆是否有帮助(如果有,如何触发)但你有更好的机会通过回购协议唤醒其中一方(无论是有问题还是在Gitter上)。你确定Dispose包装器方法会给你带来性能问题吗?这不是包装器方法,而是一种。有一种方法可以优化简单的场景,但我不会假装理解它何时起作用或不起作用。@Robert Harvey:通过计算超级指令:是的。你可以通过calli来尝试ng DoStructNofInly生成了我所期望的代码。您是否对该代码运行了探查器,并确定这是一个实际的性能问题,与您的非功能性软件需求相冲突?因为functlet对于正确的异常堆栈展开是必需的,所以您现在看到的代码可能是JIT c中最好的只要有一个
finally
就需要做。除非从事这项工作的人中有一个人能够直言不讳地指出
finally
克隆是否有帮助(如果有,如何触发),但你有更好的机会通过回购协议唤醒其中一人(无论是有问题还是在Gitter上).
您将在运行时看到巨大的差异
——我真的对此表示怀疑。对于大多数实现它的对象来说,处置是一个非常重要的操作。无论您在第一段中描述了什么(我真的不理解您所说的)不太可能有任何区别。@fredou:你能给一个代码示例你到底是什么意思吗?我尝试了一种工厂方法来加快速度,但有趣的是,只有微基准点从中受益。使用代码的真实跟踪确实变慢了。似乎正在进行许多相互冲突的优化。@Robertharvey:我已经分别为r每个场景。数字现在更接近了,但仍然存在一些差异。衡量事情是困难的,因为在使用Benchmark.Net进行此操作时,factory方法确实显示出明显的优势,但使用更复杂代码的实际用例确实降低了很多。
您将在运行时看到巨大的差异
——我真的怀疑这一点对于大多数实现osal的对象来说,osal是一个非常重要的操作