[英]C#: Does Deconstruct(…) generate extra junk assignments in the compiled output?
我正在檢查解構是否會導致在堆上實例化額外的對象,因為我在需要盡可能少的 GC 壓力的區域做某事。 這是我正在嘗試的代碼:
using System;
public struct Pair
{
public int A;
public int B;
public Pair(int a, int b)
{
A = a;
B = b;
}
public void Deconstruct(out int a, out int b)
{
a = A;
b = B;
}
}
public class Program
{
public static void Main()
{
Pair pair = new Pair(1, 2);
// Line of interest
(int a, int b) = pair;
Console.WriteLine(a + " " + b);
}
}
我通過SharpLab運行了這個,看看 C# 為我做了什么,它做了以下事情:
public static void Main()
{
Pair pair = new Pair(1, 2);
Pair pair2 = pair;
int a;
int b;
pair2.Deconstruct(out a, out b);
int num = a;
int num2 = b;
Console.WriteLine(num.ToString() + " " + num2.ToString());
}
這回答了我最初的問題,即我是否必須擔心額外的分配......但更有趣的是,發布模式(因為上面是調試)有:
public static void Main()
{
int a;
int b;
new Pair(1, 2).Deconstruct(out a, out b);
int num = a;
int num2 = b;
Console.WriteLine(num.ToString() + " " + num2.ToString());
}
然而,這可以減少到(這是我做一些額外的變量修剪num
和num2
):
public static void Main()
{
int a;
int b;
new Pair(1, 2).Deconstruct(out a, out b);
Console.WriteLine(a.ToString() + " " + b.ToString());
}
這是一個有趣的問題,因為兩個整數的額外堆棧分配對我的程序性能沒有任何意義。 雖然我嘗試查看Main
的 IL 並得到:
// Methods
.method public hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2074
// Code size 54 (0x36)
.maxstack 3
.locals init (
[0] int32,
[1] int32,
[2] valuetype Pair,
[3] int32,
[4] int32
)
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj instance void Pair::.ctor(int32, int32)
IL_0007: stloc.2
IL_0008: ldloca.s 2
IL_000a: ldloca.s 3
IL_000c: ldloca.s 4
IL_000e: call instance void Pair::Deconstruct(int32&, int32&)
IL_0013: ldloc.3
IL_0014: stloc.0
IL_0015: ldloc.s 4
IL_0017: stloc.1
IL_0018: ldloca.s 0
IL_001a: call instance string [System.Private.CoreLib]System.Int32::ToString()
IL_001f: ldstr " "
IL_0024: ldloca.s 1
IL_0026: call instance string [System.Private.CoreLib]System.Int32::ToString()
IL_002b: call string [System.Private.CoreLib]System.String::Concat(string, string, string)
IL_0030: call void [System.Console]System.Console::WriteLine(string)
IL_0035: ret
} // end of method Program::Main
JIT ASM 是
Program.Main()
L0000: push ebp
L0001: mov ebp, esp
L0003: push edi
L0004: push esi
L0005: push ebx
L0006: sub esp, 0x20
L0009: lea edi, [ebp-0x28]
L000c: call 0x68233ac
L0011: mov eax, ebp
L0013: mov [ebp-0x14], eax
L0016: push 0x3
L0018: mov dword [ebp-0x20], 0x6cce29c
L001f: mov eax, esp
L0021: mov [ebp-0x1c], eax
L0024: lea eax, [0x146004df]
L002a: mov [ebp-0x18], eax
L002d: mov byte [esi+0x8], 0x0
L0031: call dword [0x6cce680]
L0037: mov byte [esi+0x8], 0x1
L003b: cmp dword [0x621e5188], 0x0
L0042: jz L0049
L0044: call 0x62023890
L0049: xor eax, eax
L004b: mov [ebp-0x18], eax
L004e: mov byte [esi+0x8], 0x1
L0052: mov eax, [ebp-0x24]
L0055: mov [esi+0xc], eax
L0058: lea esp, [ebp-0xc]
L005b: pop ebx
L005c: pop esi
L005d: pop edi
L005e: pop ebp
L005f: ret
但是,當涉及到裝配部分時,我有點不合群。
如前所述,那里有任何中間體嗎? 我感興趣的是
int num = a;
int num2 = b;
是否完全優化。 我也對為什么編譯器會在發布版本中創建中間體(有原因嗎?)或者它是否是 SharpLab 的反編譯工件感興趣。
這適用於 GC 暫停很糟糕的游戲引擎。
由於 GC 暫停的成本很重要,因此非常清楚:您正在執行實時編程。 而且我只能說Realtime Programming和GC Memory Management不要混用。
你也許可以解決這個問題,但還會有另一個問題。 然后是另一個。 然后越來越多,直到你終於意識到自己走上了死胡同。 您越早意識到自己可能處於死胡同,您就能挽救的工作就越多。
從歷史上看,游戲引擎——尤其是繪圖代碼——是使用直接 memory 管理的非托管代碼。 .NET 代碼與位數無關。 但是一旦你使用了專業的繪圖代碼,你基本上就被它的位數束縛住了。 但是我無法判斷這是否只是慣性(我們之前使用過該引擎並且不會更改它,只是因為語言/運行時做了)還是它對性能很重要。
我也不能說有多少 Unity 繪圖代碼使用了非托管代碼。 但由於需要為特定平台構建 Unity 游戲,我將假設:多於沒有。 所以很可能在做游戲引擎時不進入非托管代碼是不可能的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.