繁体   English   中英

如果我有 ac# readonly 结构,它有非只读结构作为成员,编译器会用 in 参数创建防御性副本

[英]If I have a c# readonly struct that has non readonly structs as members, will compiler create defensive copy with in parameter

因此,如果我有一个结构体 PlayerData,它具有在 System.Numerics 中定义的 Vector3 结构体的成员(不是只读结构体)

public readonly struct PlayerData
{
   public readonly Vector3 SpawnPoint;
   public readonly Vector3 CurrentPosition;
   public readonly Vector3 Scale;    
  
   ...constructor that initializes fields
}

我将它们传递给方法:

AddPlayerData(new PlayerData(Vector3.Zero, Vector3.UnitX, Vector3.One))

public void AddPlayerData(in PlayerData playerData)
{
  ...
}

c# 是否会因为 Vector3 成员不是只读结构而是只读字段而创建防御性副本? 现有的库不提供向量的只读版本,所以如果我不为基本向量编写自己的版本,在传递大于 intptr 的结构时,我是否被迫忘记整个参数优化? 阅读时关于使用的信息不是很清楚: https : //docs.microsoft.com/en-us/dotnet/csharp/write-safe-efficient-code

有趣的问题。 让我们测试一下:

    public void AddPlayerData(in PlayerData pd)
    {
        pd.SpawnPoint.X = 42;
    }

给出编译器错误:

只读字段 'PlayerData.SpawnPoint' 的成员不能修改(构造函数或变量中除外)

由此我假设编译器不会创建任何防御性副本,因为无论如何都不能修改非只读结构。 可能存在一些允许结构更改的边缘情况,我对语言规范不够精通,无法确定这一点。

然而,讨论编译器优化是很困难的,因为只要结果相同,编译器通常可以自由地做任何它想做的事情,因此编译器版本之间的行为很可能会发生变化。 像往常一样,建议是做一个基准来比较你的替代品。

所以让我们这样做:

public readonly struct PlayerData1
{
    public readonly Vector3 A;
    public readonly Vector3 B;
    public readonly Vector3 C;
    public readonly Vector3 D;
    public readonly Vector3 E;
    public readonly Vector3 F;
    public readonly Vector3 G;
    public readonly Vector3 H;
}
public readonly struct PlayerData2
{
    public readonly ReadonlyVector3 A;
    public readonly ReadonlyVector3 B;
    public readonly ReadonlyVector3 C;
    public readonly ReadonlyVector3 D;
    public readonly ReadonlyVector3 E;
    public readonly ReadonlyVector3 F;
    public readonly ReadonlyVector3 G;
    public readonly ReadonlyVector3 H;
}

public readonly struct ReadonlyVector3
{
    public readonly float X;
    public readonly float Y;
    public readonly float Z;
}

    public static float Sum1(in PlayerData1 pd) => pd.A.X + pd.D.Y + pd.H.Z;
    public static float Sum2(in PlayerData2 pd) => pd.A.X + pd.D.Y + pd.H.Z;

    [Test]
    public void TestInParameterPerformance()
    {
        var pd1 = new PlayerData1();
        var pd2 = new PlayerData2();

        // Do warmup 
        Sum1(pd1);
        Sum2(pd2);
        float sum1 = 0;
        
        var sw1 = Stopwatch.StartNew();
        for (int i = 0; i < 1000000000; i++)
        {
            sum1 += Sum1(pd1);
        }


        float sum2 = 0;
        sw1.Stop();
        var sw2 = Stopwatch.StartNew();
        for (int i = 0; i < 1000000000; i++)
        {
            sum2 += Sum2(pd2);
        }
        sw2.Stop();

        Console.WriteLine("Sum1: " + sw1.ElapsedMilliseconds);
        Console.WriteLine("Sum2: " + sw2.ElapsedMilliseconds);
    }

对我来说,使用 .Net framework 4.8 这给

Sum1: 1035
Sum2: 1027

即在测量误差范围内。 所以我不会担心。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2025 STACKOOM.COM