Я проверял, вызывает ли деконструкция создание экземпляров дополнительных объектов в куче, поскольку я делаю что-то в области, где мне нужно иметь как можно меньшее давление G C. Это код, который я пробовал:
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());
}
Это интересный вопрос, так как нет возможности дополнительного выделения стека двух целых чисел будет означать что угодно с точки зрения производительности моей программы. Хотя ради интереса я попытался посмотреть на IL Main
и получить:
// 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.