[英]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.