C# 7.2 const 与函数参数传递中的 referenced(in) 只读字段

C# 7.2 const vs referenced(in) readonly field in function parameters passing

假设我有一个 class: 常量

private const decimal x = 2.0m;

只读字段

private readonly decimal y = 2.0m;

具有此签名的方法

void Method1(in decimal x)

如果我使用 const x Method1(x) 调用 Method1,我假设 x 的值将按值传递,而不是当我将 Method1 与只读 y Method1(in y) 一起使用时,值将通过只读参考。所以我的期望是性能更高的是用作 "fake" const 的只读字段。我错了?我怀疑 const 将在内部由编译器使用或由 clr 优化,作为通过引用传递的只读字段来执行。

我做了一个测试,赢家是传入的只读字段,这是用 i7 8700k 处理器执行的代码

class Test
{
    public const decimal x = 0.2m;
    public readonly decimal y = 0.2m;

    public void Method1(in decimal x)
    {
        Thread.MemoryBarrier();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var t = new Test();

        var sw = new Stopwatch();
        sw.Start();
        for (var i = 0; i < 100000000; i++)
        {
            t.Method1(in t.y);
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedTicks);
        sw.Restart();
        for (var i = 0; i < 100000000; i++)
        {
            t.Method1(Test.x);
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedTicks);
        Console.ReadLine();
     } 
}

作为 t.Method1(in t.y) 调用的方法;消耗了 11606428 个刻度, 调用的方法为 t.Method1(Test.x);已消耗 16963941 ticks

所以在这种情况下没有针对 const 的底层优化

是的,可能 readonly 选项的性能更高,但是对于这种性能增益是否相关还有待商榷。

为什么?

const 版本实际上会在每次调用该方法时 运行 decimal 构造函数,而 readonly / in 版本只会复制对先前创建的 decimal 实例的引用。后者显然更快

您可以通过简单地检查 IL 来验证这一点:

  • 常量版本:

    IL_0001: ldc.i4.s 20
    IL_0003: ldc.i4.0
    IL_0004: ldc.i4.0
    IL_0005: ldc.i4.0
    IL_0006: ldc.i4.1
    IL_0007: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    IL_000c: call instance bool C::Method2(valuetype [mscorlib]System.Decimal)
    
  • readonly/in版本:

    IL_0002: ldflda valuetype [mscorlib]System.Decimal C::dd
    IL_0007: call instance bool C::Method1(valuetype [mscorlib]System.Decimal&)
    IL_000c: pop
    

就是说,我不太确定为什么要将 const 与性能挂钩。 const 是一种在您的代码中传达给定值 永远不会改变的方式 const 并不意味着,这个存储为常量的值在使用时会更高效。

除非你真的有一个经验证明的性能问题可以通过这种边际(充其量)优化来解决,一个逻辑上是常量的值应该是 const,而一个变量在逻辑上是一个只读值应为 readonly,应忽略所有其他注意事项。如果您的情况是前者,那么请在您的代码中清楚地记录为什么将逻辑常量值实现为只读字段。