RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 891407
Accepted
A1essandro
A1essandro
Asked:2020-10-10 22:53:13 +0000 UTC2020-10-10 22:53:13 +0000 UTC 2020-10-10 22:53:13 +0000 UTC

C# 中的密封、虚拟、非虚拟方法和性能

  • 772

任务是最大化应用程序某个部分的性能。在阅读了一些Habr文章之后,在某处听到或阅读了一些东西后,我开始密封类和方法(并将一些小类变成结构)。已经提交后,我决定在一个小应用程序中检查这是否真的给出了结果。

class X
{
    public int NonVirtual() => DateTime.Now.Millisecond;
    public virtual int Virtual() => DateTime.Now.Minute;
}

class Y : X
{
    public override int Virtual() => DateTime.Now.Millisecond;
}

class Program
{

    private static volatile int TST = 0;

    static int Slow(Y x) => x.Virtual();

    static int Fast(Y y) => y.NonVirtual();

    static void Main(string[] args)
    {

        int i = 0;
        var stopwatch1 = new Stopwatch();
        var stopwatch2 = new Stopwatch();

        stopwatch1.Start();
        var y = new Y();
        for (i = 0; i < 100000000; i++)
        {
            TST = Fast(y);
        }
        stopwatch1.Stop();
        Console.WriteLine("Time elapsed: {0}", stopwatch1.Elapsed);

        stopwatch2.Start();
        for (i = 0; i < 100000000; i++)
        {
            TST = Slow(y);
        }
        stopwatch2.Stop();
        Console.WriteLine("Time elapsed: {0}", stopwatch2.Elapsed);

        Console.ReadKey();
    }
}

该字段TST被声明为volatile防止编译器优化调用。

起初我很惊讶根本没有区别,然后第一个周期快了几十毫秒,然后是第二个(尽管虚拟方法表和所有这些,假设虚拟方法应该是合乎逻辑的至少落后一点)。

然后我进入了 IL:

IL_0001: callvirt  instance int32 PerformanceTests.X::Virtual()

这是一个虚方法调用。感觉真爽。第二次调用:

IL_0001: callvirt  instance int32 PerformanceTests.X::NonVirtual()

对不起,什么?callvirt? 不应该在这里call吗?sealed也不会以任何方式影响虚方法的调用。

想问问比较有经验的同事,毕竟封课在性能方面有什么意义吗?另外,为什么在 IL 中调用非虚拟函数与调用虚拟函数相同?

更新: @Grundy 在评论中写道callvirt- 因为该方法是在基类中声明的。重写代码,以便现在使用基类(即Y- 未使用)。callvirt所以它被调用。

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Zergatul
    2020-10-10T23:44:07Z2020-10-10T23:44:07Z

    正如评论中已经提到的,编译器用于callvirt生成NullReferenceException.

    要获得纯call语句,编译器必须确保类实例不能是null. 例子:

    class Test
    {
        public void Method() => Console.WriteLine(123);
    }
    
    static void Main(string[] args)
    {
        new Test().Method();
    }
    

    IL-编码:

    IL_0001: newobj instance void ConsoleApp1.Program/Test::.ctor()
    IL_0006: call instance void ConsoleApp1.Program/Test::Method()
    

    如果代码稍作改动:

    static Test GetTest() => new Test();
    
    static void Main(string[] args)
    {
        GetTest().Method();
    }
    

    我们已经得到callvirt了,因为编译器假定它GetTest可以返回null:

    IL_0001: call class ConsoleApp1.Program/Test ConsoleApp1.Program::GetTest()
    IL_0006: callvirt instance void ConsoleApp1.Program/Test::Method()
    

    sealed不会影响运行时的任何内容。它只是一个开发人员标记,告诉您在高级代码中可以做什么和不可以做什么。

    对于每个非callvirt虚拟方法JIT,在每个方法之前插入一条附加指令call:

    cmp         dword ptr [/*здесь регистр с адресом экземляра*/],ecx  
    call        00007FF9CD540098  // метод
    

    单cmp条指令的影响很小。

    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5