简体   繁体   English

C#:Deconstruct(…) 是否会在编译的 output 中生成额外的垃圾分配?

[英]C#: Does Deconstruct(…) generate extra junk assignments in the compiled output?

I was checking whether or not deconstruction causes extra objects to be instantiated on the heap since I am doing something in an area where I need to have as little GC pressure as possible.我正在检查解构是否会导致在堆上实例化额外的对象,因为我在需要尽可能少的 GC 压力的区域做某事。 This is the code I was trying out:这是我正在尝试的代码:

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);
    }
}

I ran this through SharpLab to see what C# is doing for me, and it does the following:我通过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());
}

This answered my original question of whether or not I have to worry about extra allocations... but then even more interestingly, the release mode (since the above is debug) has:这回答了我最初的问题,即我是否必须担心额外的分配......但更有趣的是,发布模式(因为上面是调试)有:

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());
}

However this can be reduced down to (this is me doing some extra variable pruning num and num2 ):然而,这可以减少到(这是我做一些额外的变量修剪numnum2 ):

public static void Main()
{
    int a;
    int b;
    new Pair(1, 2).Deconstruct(out a, out b);
    Console.WriteLine(a.ToString() + " " + b.ToString());
}

This is a question of interest, since there is no way extra stack allocation of two integers is going to mean anything in terms of my program performance.这是一个有趣的问题,因为两个整数的额外堆栈分配对我的程序性能没有任何意义。 For fun though I tried looking at the IL of Main and get:虽然我尝试查看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

and the JIT ASM is 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

However I'm a bit out of my league when it comes to the assembly part.但是,当涉及到装配部分时,我有点不合群。

Is there any intermediate there as seen as discussed earlier?如前所述,那里有任何中间体吗? I'm interested in if the我感兴趣的是

int num = a;
int num2 = b;

were completely optimized out or not.是否完全优化。 I'm also interested in why the compiler would create intermediates in the release version (is there a reason?) or if it's a decompilation artifact from SharpLab.我也对为什么编译器会在发布版本中创建中间体(有原因吗?)或者它是否是 SharpLab 的反编译工件感兴趣。

This is for a game engine where GC pauses are bad.这适用于 GC 暂停很糟糕的游戏引擎。

As the cost of a GC pause maters, it is abundantly clear: You are doing realtime programming .由于 GC 暂停的成本很重要,因此非常清楚:您正在执行实时编程 And I can only say that Realtime Programming and GC Memory Management do not mix.而且我只能说Realtime Programming和GC Memory Management不要混用。

You might be able to fix this problem, but there is going to be another one.你也许可以解决这个问题,但还会有另一个问题。 And then another one after that.然后是另一个。 And then more and more, until you finally realize you were on a dead end.然后越来越多,直到你终于意识到自己走上了死胡同。 The sooner you realize that you are likely on a dead end, the more work you will be able to salvage.您越早意识到自己可能处于死胡同,您就能挽救的工作就越多。

Historically game engines - especially the drawing code - are unmanaged code that uses direct memory management.从历史上看,游戏引擎——尤其是绘图代码——是使用直接 memory 管理的非托管代码。 .NET Code is bitness agnostic. .NET 代码与位数无关。 But once you used professional drawing code, you were basically tied down to its bitness.但是一旦你使用了专业的绘图代码,你基本上就被它的位数束缚住了。 However I can not tell if that was just inertia (we used that engine before and will not change it, just because the language/runtime did) or if it was important for performance.但是我无法判断这是否只是惯性(我们之前使用过该引擎并且不会更改它,只是因为语言/运行时做了)还是它对性能很重要。

I also can not say how much of Unity drawing code uses unmanaged code.我也不能说有多少 Unity 绘图代码使用了非托管代码。 But as Unity games need to be built for specific platforms, I am going to assume: More than none.但由于需要为特定平台构建 Unity 游戏,我将假设:多于没有。 So it might well be that not going into unmanaged code is impossible when doing game engines.所以很可能在做游戏引擎时不进入非托管代码是不可能的。

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

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