有没有办法内联元组解构以避免不必要的分配?
Is there a way to inline tuple deconstruction to avoid an unnecessary allocation?
我有下面的例子struct
:
struct Data {
internal long a;
internal long b;
internal void Deconstruct(out long aa, out long bb) {
aa = a; bb = b;
}
}
如果我只想使用结构的值而忘记结构本身怎么办?
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 编译(这个 class 库),一个 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 WhosebugDemo.Q1/Data,
[2] int64,
[3] int64
)
IL_0000: ldarg.0
IL_0001: call instance valuetype WhosebugDemo.Q1/Data WhosebugDemo.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 WhosebugDemo.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
如果你想忘记结构,而不是
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);
}
根据 mikez 的推荐,我使用 windbg
和 sos
扩展来查看 jitted 代码。
我从中学到了三件事:
- jitting后,在这种情况下堆栈上没有分配结构
- 查看生成的 il 代码不足以推断运行时执行的代码
- 这个简化的示例可能与我原来的用例不同 - 所以我必须查看我的原始示例的 jitted 代码
感谢您提供的所有有用信息。
在不进一步修改原始源代码的情况下,这是 Test
方法的 !Name2EE
的输出:
C:\Users\...\source\repos\WhosebugDemo\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 (WhosebugDemo.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\WhosebugDemo\Q1.cs @ 38:
00007ffa`02c41d25 e8eee5ffff call 00007ffa`02c40318 (System.Console.WriteLine(Int64), mdToken: 0000000006000080)
C:\Users\...\source\repos\WhosebugDemo\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\WhosebugDemo\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\WhosebugDemo\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
据我了解,堆栈上没有分配结构。
我有下面的例子struct
:
struct Data {
internal long a;
internal long b;
internal void Deconstruct(out long aa, out long bb) {
aa = a; bb = b;
}
}
如果我只想使用结构的值而忘记结构本身怎么办?
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 编译(这个 class 库),一个 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 WhosebugDemo.Q1/Data,
[2] int64,
[3] int64
)
IL_0000: ldarg.0
IL_0001: call instance valuetype WhosebugDemo.Q1/Data WhosebugDemo.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 WhosebugDemo.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
如果你想忘记结构,而不是
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);
}
根据 mikez 的推荐,我使用 windbg
和 sos
扩展来查看 jitted 代码。
我从中学到了三件事:
- jitting后,在这种情况下堆栈上没有分配结构
- 查看生成的 il 代码不足以推断运行时执行的代码
- 这个简化的示例可能与我原来的用例不同 - 所以我必须查看我的原始示例的 jitted 代码
感谢您提供的所有有用信息。
在不进一步修改原始源代码的情况下,这是 Test
方法的 !Name2EE
的输出:
C:\Users\...\source\repos\WhosebugDemo\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 (WhosebugDemo.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\WhosebugDemo\Q1.cs @ 38:
00007ffa`02c41d25 e8eee5ffff call 00007ffa`02c40318 (System.Console.WriteLine(Int64), mdToken: 0000000006000080)
C:\Users\...\source\repos\WhosebugDemo\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\WhosebugDemo\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\WhosebugDemo\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
据我了解,堆栈上没有分配结构。