有没有办法内联元组解构来避免不必要的分配?

时间:2018-02-27 23:42:35

标签: c# struct tuples inline c#-7.0

我有以下示例struct

struct Data {
    internal long a;
    internal long b;

    internal void Deconstruct(out long aa, out long bb) {
        aa = a; bb = b;
    }
}

如果我只想使用struct的值而忘记struct本身怎么办?

Data Generate()
    => new Data() { a = 3, b = 5 };

void Test() {
    (var a, var b) = Generate();
    Console.WriteLine(a);
    Console.WriteLine(b);
}

Generate的调用会创建一个结构,并在其各个部分立即进行分解。我可以以某种方式内联这个过程并完全摆脱结构吗?

我在发布模式下使用VS 15.5.7编译(此类库)ilspy显示:

.method private hidebysig 
    instance void Test () cil managed 
{
    // Method begins at RVA 0x208c
    // Code size 33 (0x21)
    .maxstack 3
    .locals init (
        [0] int64,
        [1] valuetype StackOverflowDemo.Q1/Data,
        [2] int64,
        [3] int64
    )

    IL_0000: ldarg.0
    IL_0001: call instance valuetype StackOverflowDemo.Q1/Data StackOverflowDemo.Q1::Generate()
    IL_0006: stloc.1
    IL_0007: ldloca.s 1
    IL_0009: ldloca.s 2
    IL_000b: ldloca.s 3
    IL_000d: call instance void StackOverflowDemo.Q1/Data::Deconstruct(int64&, int64&)
    IL_0012: ldloc.2
    IL_0013: ldloc.3
    IL_0014: stloc.0
    IL_0015: call void [System.Console]System.Console::WriteLine(int64)
    IL_001a: ldloc.0
    IL_001b: call void [System.Console]System.Console::WriteLine(int64)
    IL_0020: ret
} // end of method Q1::Test

2 个答案:

答案 0 :(得分:4)

如果你想忘记结构,而不是

Data Generate()
    => new Data() { a = 3, b = 5 };

为什么不使用

void Generate(out long aa, out long bb)
{
    aa = 3; bb = 5;
}

这消除了你提出的结构。

如果你想保留结构但摆脱额外的分配,你也可以这样做:

class Data {
    public long A { get; internal set; }
    public long B { get; internal set; }

    internal Data(long a, long b) {
        A = a; B = b;
    }
}

然后使用:

void Test() {
    var data = Generate();
    Console.WriteLine(data.A);
    Console.WriteLine(data.B);
}

答案 1 :(得分:0)

根据 mikez 的建议,我使用windbgsos扩展程序查看了jitted代码。

我从中学到了三件事:

  • 在jitting之后,在这种情况下没有在堆栈上分配结构
  • 查看生成的il代码不足以推断运行时执行的代码
  • 这个简化示例可能与我原来的用例不同 - 所以我必须查看原始示例的jitted代码

感谢您提供所有有用的意见。

如果不对原始源代码进行进一步修改,这就是!Name2EE方法的Test输出:

C:\Users\...\source\repos\StackOverflowDemo\Q1.cs @ 37:
>>> 00007ffa`02c41d00 56              push    rsi
00007ffa`02c41d01 4883ec30        sub     rsp,30h
00007ffa`02c41d05 33c0            xor     eax,eax
00007ffa`02c41d07 4889442420      mov     qword ptr [rsp+20h],rax
00007ffa`02c41d0c 4889442428      mov     qword ptr [rsp+28h],rax
00007ffa`02c41d11 488d542420      lea     rdx,[rsp+20h]
00007ffa`02c41d16 e8dde6ffff      call    00007ffa`02c403f8 (StackOverflowDemo.Q1.Generate(), mdToken: 0000000006000003)
00007ffa`02c41d1b 488b4c2420      mov     rcx,qword ptr [rsp+20h]
00007ffa`02c41d20 488b742428      mov     rsi,qword ptr [rsp+28h]

C:\Users\...\source\repos\StackOverflowDemo\Q1.cs @ 38:
00007ffa`02c41d25 e8eee5ffff      call    00007ffa`02c40318 (System.Console.WriteLine(Int64), mdToken: 0000000006000080)

C:\Users\...\source\repos\StackOverflowDemo\Q1.cs @ 39:
00007ffa`02c41d2a 488bce          mov     rcx,rsi
00007ffa`02c41d2d e8e6e5ffff      call    00007ffa`02c40318 (System.Console.WriteLine(Int64), mdToken: 0000000006000080)

C:\Users\...\source\repos\StackOverflowDemo\Q1.cs @ 40:
00007ffa`02c41d32 90              nop
00007ffa`02c41d33 4883c430        add     rsp,30h
00007ffa`02c41d37 5e              pop     rsi
00007ffa`02c41d38 c3              ret

对于Generate方法:

C:\Users\...\source\repos\StackOverflowDemo\Q1.cs @ 28:
>>> 00007ffa`02c41d50 b803000000      mov     eax,3
00007ffa`02c41d55 b905000000      mov     ecx,5
00007ffa`02c41d5a 488902          mov     qword ptr [rdx],rax
00007ffa`02c41d5d 48894a08        mov     qword ptr [rdx+8],rcx
00007ffa`02c41d61 488bc2          mov     rax,rdx
00007ffa`02c41d64 c3              ret

据我所知,堆栈上没有分配结构。