为什么速记访问器功能比常规访问器更快?

时间:2016-05-31 23:36:58

标签: c# clr

我进行了一项测试,测量下面两个访问器功能之间的速度差异,时间差异大于我的预期。我只是觉得速记实现可能会快一点,所以我想测试一下。

我测量了所需的总秒数,为每个类调用Get函数,进行10亿次迭代。

using System;
using System.Diagnostics;

class SimpleGet {
    int value;

    public int Get() {
        return value;
    }
}

class ShorthandGet {
    int value;

    public int Get() => value;
}

class Program {
    static void Main() {
        const int Iterations = 1000000000;
        Stopwatch sw = new Stopwatch();

        sw.Start();
        {
            int n; SimpleGet sg = new SimpleGet();
            for (int i = 0; i < Iterations; i++) {
                n = sg.Get();
            }
        }
        sw.Stop();
        Console.WriteLine("SimpleGet: " + sw.Elapsed.TotalSeconds);

        sw.Reset();

        sw.Start();
        {
            int n; ShorthandGet shg = new ShorthandGet();
            for (int i = 0; i < Iterations; i++) {
                n = shg.Get();
            }
        }
        sw.Stop();
        Console.WriteLine("ShorthandGet: " + sw.Elapsed.TotalSeconds);

        Console.ReadLine();

    }
}

结果:

// 1 billion iterations
SimpleGet: 11.8484244
ShorthandGet: 4.3218568

速度的差异是巨大的。我能看到的唯一区别是常规函数有括号,因此在函数调用中创建一个新的作用域。由于范围内没有新变量,理论上不应该忽视&#34;忽视&#34;有人可以解释为什么常规功能没有被优化到与另一个相同的水平吗?

修改

我使用属性测试了相同的方案:Value { get { return value; } }Value => value;,时间差异非常接近各自的功能时差。我认为原因是一样的。

1 个答案:

答案 0 :(得分:8)

简短的回答是,正确完成基准测试没有区别。

对于像这样的微优化案例,我总是先看看IL。不是因为你会得到一些深刻的见解,而是因为如果生成相同的IL,那么在运行时应该没有差别。接下来要记住的是,您必须从Release版本开始,因为编译器将删除这些版本中不必要的IL指令。

在Debug构建中,长格式IL(SimpleGet)有额外的指令来启用断点:

.method public hidebysig 
    instance int32 Get () cil managed 
{
    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldfld int32 ConsoleApplication7.SimpleGet::'value'
    IL_0007: stloc.0
    IL_0008: br.s IL_000a
    IL_000a: ldloc.0
    IL_000b: ret
}

与ShorthandGet相比要短得多:

.method public hidebysig 
    instance int32 Get () cil managed 
{
    IL_0000: ldarg.0
    IL_0001: ldfld int32 ConsoleApplication7.ShorthandGet::'value'
    IL_0006: ret
}

然而,在优化构建中,两种形式都会产生与上述ShorthandGet相同的IL。

Debug版本的基准测试可能会显示您所演示的差异,但这些内容永远不值得比较,因为如果您关心性能,那么您将运行Release版本的优化代码。故事的寓意是始终对优化的代码进行性能分析。经常遗漏的另一个项目是在没有附加调试器的情况下进行基准测试,因为即使对于优化的IL,JIT也会检测调试器并发出更多可调试的机器代码。许多人都错过了这个,因为他们只需点击“开始”或在VS中点击F5,但是这个启动附带调试器的程序。使用菜单选项Debug&gt;启动时不进行调试。