Last active
January 22, 2018 10:00
-
-
Save gfoidl/6decb36753b3e3098e899f9a0894e852 to your computer and use it in GitHub Desktop.
Struct argument copy & byref
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.CompilerServices; | |
namespace ConsoleApp2 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var foo1 = new Foo1(1, 2, 3); | |
Console.WriteLine(Length1(foo1)); | |
foo1 = new Foo1(4, 5, 6); | |
Console.WriteLine(Length2(foo1)); | |
foo1 = new Foo1(7, 8, 9); | |
Console.WriteLine(Length21(ref foo1)); | |
var foo2 = new Foo2(1, 2, 3); | |
Console.WriteLine(Length3(foo2)); | |
foo2 = new Foo2(4, 5, 6); | |
Console.WriteLine(Length4(foo2)); | |
} | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static int Length1(Foo1 foo) => foo.X + foo.Y + foo.Z; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static int Length2(in Foo1 foo) => foo.X + foo.Y + foo.Z; | |
private static int Length21(ref Foo1 foo) => foo.X + foo.Y + foo.Z; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static int Length3(Foo2 foo) => foo.X + foo.Y + foo.Z; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static int Length4(in Foo2 foo) => foo.X + foo.Y + foo.Z; | |
} | |
public struct Foo1 | |
{ | |
public int X { get; } | |
public int Y { get; } | |
public int Z { get; } | |
public Foo1(int x, int y, int z) | |
{ | |
this.X = x; | |
this.Y = y; | |
this.Z = z; | |
} | |
} | |
public readonly struct Foo2 | |
{ | |
public int X { get; } | |
public int Y { get; } | |
public int Z { get; } | |
public Foo2(int x, int y, int z) | |
{ | |
this.X = x; | |
this.Y = y; | |
this.Z = z; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.class private auto ansi beforefieldinit ConsoleApp2.Program | |
extends [System.Runtime]System.Object | |
{ | |
// Methods | |
.method private hidebysig static | |
void Main ( | |
string[] args | |
) cil managed | |
{ | |
// Method begins at RVA 0x2050 | |
// Code size 110 (0x6e) | |
.maxstack 4 | |
.entrypoint | |
.locals init ( | |
[0] valuetype ConsoleApp2.Foo1, | |
[1] valuetype ConsoleApp2.Foo2 | |
) | |
IL_0000: ldloca.s 0 | |
IL_0002: ldc.i4.1 | |
IL_0003: ldc.i4.2 | |
IL_0004: ldc.i4.3 | |
IL_0005: call instance void ConsoleApp2.Foo1::.ctor(int32, int32, int32) | |
IL_000a: ldloc.0 | |
IL_000b: call int32 ConsoleApp2.Program::Length1(valuetype ConsoleApp2.Foo1) | |
IL_0010: call void [System.Console]System.Console::WriteLine(int32) | |
IL_0015: ldloca.s 0 | |
IL_0017: ldc.i4.4 | |
IL_0018: ldc.i4.5 | |
IL_0019: ldc.i4.6 | |
IL_001a: call instance void ConsoleApp2.Foo1::.ctor(int32, int32, int32) | |
IL_001f: ldloca.s 0 | |
IL_0021: call int32 ConsoleApp2.Program::Length2(valuetype ConsoleApp2.Foo1&) | |
IL_0026: call void [System.Console]System.Console::WriteLine(int32) | |
IL_002b: ldloca.s 0 | |
IL_002d: ldc.i4.7 | |
IL_002e: ldc.i4.8 | |
IL_002f: ldc.i4.s 9 | |
IL_0031: call instance void ConsoleApp2.Foo1::.ctor(int32, int32, int32) | |
IL_0036: ldloca.s 0 | |
IL_0038: call int32 ConsoleApp2.Program::Length21(valuetype ConsoleApp2.Foo1&) | |
IL_003d: call void [System.Console]System.Console::WriteLine(int32) | |
IL_0042: ldloca.s 1 | |
IL_0044: ldc.i4.1 | |
IL_0045: ldc.i4.2 | |
IL_0046: ldc.i4.3 | |
IL_0047: call instance void ConsoleApp2.Foo2::.ctor(int32, int32, int32) | |
IL_004c: ldloc.1 | |
IL_004d: call int32 ConsoleApp2.Program::Length3(valuetype ConsoleApp2.Foo2) | |
IL_0052: call void [System.Console]System.Console::WriteLine(int32) | |
IL_0057: ldloca.s 1 | |
IL_0059: ldc.i4.4 | |
IL_005a: ldc.i4.5 | |
IL_005b: ldc.i4.6 | |
IL_005c: call instance void ConsoleApp2.Foo2::.ctor(int32, int32, int32) | |
IL_0061: ldloca.s 1 | |
IL_0063: call int32 ConsoleApp2.Program::Length4(valuetype ConsoleApp2.Foo2&) | |
IL_0068: call void [System.Console]System.Console::WriteLine(int32) | |
IL_006d: ret | |
} // end of method Program::Main | |
.method private hidebysig static | |
int32 Length1 ( | |
valuetype ConsoleApp2.Foo1 foo | |
) cil managed noinlining | |
{ | |
// Method begins at RVA 0x20ca | |
// Code size 24 (0x18) | |
.maxstack 8 | |
IL_0000: ldarga.s foo | |
IL_0002: call instance int32 ConsoleApp2.Foo1::get_X() | |
IL_0007: ldarga.s foo | |
IL_0009: call instance int32 ConsoleApp2.Foo1::get_Y() | |
IL_000e: add | |
IL_000f: ldarga.s foo | |
IL_0011: call instance int32 ConsoleApp2.Foo1::get_Z() | |
IL_0016: add | |
IL_0017: ret | |
} // end of method Program::Length1 | |
.method private hidebysig static | |
int32 Length2 ( | |
valuetype ConsoleApp2.Foo1& foo | |
) cil managed noinlining | |
{ | |
.param [1] | |
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( | |
01 00 00 00 | |
) | |
// Method begins at RVA 0x20e4 | |
// Code size 45 (0x2d) | |
.maxstack 2 | |
.locals init ( | |
[0] valuetype ConsoleApp2.Foo1 | |
) | |
IL_0000: ldarg.0 | |
IL_0001: ldobj ConsoleApp2.Foo1 | |
IL_0006: stloc.0 | |
IL_0007: ldloca.s 0 | |
IL_0009: call instance int32 ConsoleApp2.Foo1::get_X() | |
IL_000e: ldarg.0 | |
IL_000f: ldobj ConsoleApp2.Foo1 | |
IL_0014: stloc.0 | |
IL_0015: ldloca.s 0 | |
IL_0017: call instance int32 ConsoleApp2.Foo1::get_Y() | |
IL_001c: add | |
IL_001d: ldarg.0 | |
IL_001e: ldobj ConsoleApp2.Foo1 | |
IL_0023: stloc.0 | |
IL_0024: ldloca.s 0 | |
IL_0026: call instance int32 ConsoleApp2.Foo1::get_Z() | |
IL_002b: add | |
IL_002c: ret | |
} // end of method Program::Length2 | |
.method private hidebysig static | |
int32 Length21 ( | |
valuetype ConsoleApp2.Foo1& foo | |
) cil managed | |
{ | |
// Method begins at RVA 0x211d | |
// Code size 21 (0x15) | |
.maxstack 8 | |
IL_0000: ldarg.0 | |
IL_0001: call instance int32 ConsoleApp2.Foo1::get_X() | |
IL_0006: ldarg.0 | |
IL_0007: call instance int32 ConsoleApp2.Foo1::get_Y() | |
IL_000c: add | |
IL_000d: ldarg.0 | |
IL_000e: call instance int32 ConsoleApp2.Foo1::get_Z() | |
IL_0013: add | |
IL_0014: ret | |
} // end of method Program::Length21 | |
.method private hidebysig static | |
int32 Length3 ( | |
valuetype ConsoleApp2.Foo2 foo | |
) cil managed noinlining | |
{ | |
// Method begins at RVA 0x2133 | |
// Code size 24 (0x18) | |
.maxstack 8 | |
IL_0000: ldarga.s foo | |
IL_0002: call instance int32 ConsoleApp2.Foo2::get_X() | |
IL_0007: ldarga.s foo | |
IL_0009: call instance int32 ConsoleApp2.Foo2::get_Y() | |
IL_000e: add | |
IL_000f: ldarga.s foo | |
IL_0011: call instance int32 ConsoleApp2.Foo2::get_Z() | |
IL_0016: add | |
IL_0017: ret | |
} // end of method Program::Length3 | |
.method private hidebysig static | |
int32 Length4 ( | |
valuetype ConsoleApp2.Foo2& foo | |
) cil managed noinlining | |
{ | |
.param [1] | |
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( | |
01 00 00 00 | |
) | |
// Method begins at RVA 0x214c | |
// Code size 21 (0x15) | |
.maxstack 8 | |
IL_0000: ldarg.0 | |
IL_0001: call instance int32 ConsoleApp2.Foo2::get_X() | |
IL_0006: ldarg.0 | |
IL_0007: call instance int32 ConsoleApp2.Foo2::get_Y() | |
IL_000c: add | |
IL_000d: ldarg.0 | |
IL_000e: call instance int32 ConsoleApp2.Foo2::get_Z() | |
IL_0013: add | |
IL_0014: ret | |
} // end of method Program::Length4 | |
.method public hidebysig specialname rtspecialname | |
instance void .ctor () cil managed | |
{ | |
// Method begins at RVA 0x2162 | |
// Code size 7 (0x7) | |
.maxstack 8 | |
IL_0000: ldarg.0 | |
IL_0001: call instance void [System.Runtime]System.Object::.ctor() | |
IL_0006: ret | |
} // end of method Program::.ctor | |
} // end of class ConsoleApp2.Program |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Assembly listing for method ConsoleApp2.Program:Main(ref) | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rbp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
;* V00 arg0 [V00 ] ( 0, 0 ) ref -> zero-ref class-hnd | |
; V01 loc0 [V01 ] ( 14, 14 ) struct (16) [rbp-0x18] do-not-enreg[XS] must-init addr-exposed ld-addr-op | |
; V02 loc1 [V02 ] ( 10, 10 ) struct (16) [rbp-0x28] do-not-enreg[XS] must-init addr-exposed ld-addr-op | |
; V03 tmp0 [V03 ] ( 6, 6 ) int -> [rbp-0x18] do-not-enreg[X] addr-exposed V01.<X>k__BackingField(offs=0x00) P-DEP | |
; V04 tmp1 [V04 ] ( 6, 6 ) int -> [rbp-0x14] do-not-enreg[X] addr-exposed V01.<Y>k__BackingField(offs=0x04) P-DEP | |
; V05 tmp2 [V05 ] ( 6, 6 ) int -> [rbp-0x10] do-not-enreg[X] addr-exposed V01.<Z>k__BackingField(offs=0x08) P-DEP | |
; V06 tmp3 [V06 ] ( 4, 4 ) int -> [rbp-0x28] do-not-enreg[X] addr-exposed V02.<X>k__BackingField(offs=0x00) P-DEP | |
; V07 tmp4 [V07 ] ( 4, 4 ) int -> [rbp-0x24] do-not-enreg[X] addr-exposed V02.<Y>k__BackingField(offs=0x04) P-DEP | |
; V08 tmp5 [V08 ] ( 4, 4 ) int -> [rbp-0x20] do-not-enreg[X] addr-exposed V02.<Z>k__BackingField(offs=0x08) P-DEP | |
; V09 tmp6 [V09 ] ( 4, 8 ) struct (16) [rbp-0x38] do-not-enreg[XSB] addr-exposed | |
; V10 tmp7 [V10,T00] ( 4, 8 ) byref -> rdi stack-byref | |
; V11 tmp8 [V11 ] ( 4, 8 ) struct (16) [rbp-0x48] do-not-enreg[XSB] addr-exposed | |
; V12 tmp9 [V12,T01] ( 4, 8 ) byref -> rdi stack-byref | |
;# V13 OutArgs [V13 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; | |
; Lcl frame size = 72 | |
G_M36834_IG01: | |
55 push rbp | |
4155 push r13 | |
4883EC48 sub rsp, 72 | |
488D6C2450 lea rbp, [rsp+50H] | |
4C8BEF mov r13, rdi | |
488D7DD8 lea rdi, [rbp-28H] | |
B908000000 mov ecx, 8 | |
33C0 xor rax, rax | |
F3AB rep stosd | |
498BFD mov rdi, r13 | |
G_M36834_IG02: | |
C745E801000000 mov dword ptr [rbp-18H], 1 ; var foo1 = new Foo1(1, 2, 3); | |
C745EC02000000 mov dword ptr [rbp-14H], 2 | |
C745F003000000 mov dword ptr [rbp-10H], 3 | |
488D7DC8 lea rdi, bword ptr [rbp-38H] ; begin copy of struct | |
8B75E8 mov esi, dword ptr [rbp-18H] | |
8937 mov dword ptr [rdi], esi | |
8B75EC mov esi, dword ptr [rbp-14H] | |
897704 mov dword ptr [rdi+4], esi | |
8B75F0 mov esi, dword ptr [rbp-10H] | |
897708 mov dword ptr [rdi+8], esi ; end copy of struct | |
488B7DC8 mov rdi, qword ptr [rbp-38H] | |
8B75D0 mov esi, dword ptr [rbp-30H] | |
E87BFAFFFF call ConsoleApp2.Program:Length1(struct):int | |
8BF8 mov edi, eax | |
E834FBFFFF call System.Console:WriteLine(int) | |
C745E804000000 mov dword ptr [rbp-18H], 4 ; foo1 = new Foo1(4, 5, 6); | |
C745EC05000000 mov dword ptr [rbp-14H], 5 | |
C745F006000000 mov dword ptr [rbp-10H], 6 | |
488D7DE8 lea rdi, bword ptr [rbp-18H] ; note no copy | |
E85EFAFFFF call ConsoleApp2.Program:Length2(byref):int | |
8BF8 mov edi, eax | |
E80FFBFFFF call System.Console:WriteLine(int) | |
C745E807000000 mov dword ptr [rbp-18H], 7 ; foo1 = new Foo1(7, 8, 9); | |
C745EC08000000 mov dword ptr [rbp-14H], 8 | |
C745F009000000 mov dword ptr [rbp-10H], 9 | |
488D7DE8 lea rdi, bword ptr [rbp-18H] ; note no copy | |
E841FAFFFF call ConsoleApp2.Program:Length21(byref):int | |
8BF8 mov edi, eax | |
E8EAFAFFFF call System.Console:WriteLine(int) | |
C745D801000000 mov dword ptr [rbp-28H], 1 ; var foo2 = new Foo2(1, 2, 3); | |
C745DC02000000 mov dword ptr [rbp-24H], 2 | |
C745E003000000 mov dword ptr [rbp-20H], 3 | |
488D7DB8 lea rdi, bword ptr [rbp-48H] ; begin copy of struct | |
8B75D8 mov esi, dword ptr [rbp-28H] | |
8937 mov dword ptr [rdi], esi | |
8B75DC mov esi, dword ptr [rbp-24H] | |
897704 mov dword ptr [rdi+4], esi | |
8B75E0 mov esi, dword ptr [rbp-20H] | |
897708 mov dword ptr [rdi+8], esi ; end copy of struct | |
488B7DB8 mov rdi, qword ptr [rbp-48H] | |
8B75C0 mov esi, dword ptr [rbp-40H] | |
E80CFAFFFF call ConsoleApp2.Program:Length3(struct):int | |
8BF8 mov edi, eax | |
E8ADFAFFFF call System.Console:WriteLine(int) | |
C745D804000000 mov dword ptr [rbp-28H], 4 | |
C745DC05000000 mov dword ptr [rbp-24H], 5 | |
C745E006000000 mov dword ptr [rbp-20H], 6 | |
488D7DD8 lea rdi, bword ptr [rbp-28H] ; note no copy | |
E8EFF9FFFF call ConsoleApp2.Program:Length4(byref):int | |
8BF8 mov edi, eax | |
E888FAFFFF call System.Console:WriteLine(int) | |
90 nop | |
G_M36834_IG03: | |
488D65F8 lea rsp, [rbp-08H] | |
415D pop r13 | |
5D pop rbp | |
C3 ret | |
; Total bytes of code 273, prolog size 31 for method ConsoleApp2.Program:Main(ref) | |
; ============================================================ | |
; Assembly listing for method ConsoleApp2.Program:Length1(struct):int | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rsp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
; V00 arg0 [V00,T00] ( 5, 5 ) struct (16) [rsp+0x08] do-not-enreg[SF] ld-addr-op | |
; V01 tmp0 [V01,T01] ( 2, 4 ) int -> rax | |
; V02 tmp1 [V02,T02] ( 2, 4 ) int -> rax | |
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; | |
; Lcl frame size = 24 | |
G_M42526_IG01: | |
4883EC18 sub rsp, 24 | |
48897C2408 mov qword ptr [rsp+08H], rdi | |
89742410 mov dword ptr [rsp+10H], esi | |
G_M42526_IG02: | |
8B442408 mov eax, dword ptr [rsp+08H] | |
0344240C add eax, dword ptr [rsp+0CH] | |
03442410 add eax, dword ptr [rsp+10H] | |
G_M42526_IG03: | |
4883C418 add rsp, 24 | |
C3 ret | |
; Total bytes of code 30, prolog size 4 for method ConsoleApp2.Program:Length1(struct):int | |
; ============================================================ | |
; Assembly listing for method ConsoleApp2.Program:Length2(byref):int | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rsp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
; V00 arg0 [V00,T00] ( 20, 20 ) byref -> rdi | |
;* V01 loc0 [V01 ] ( 0, 0 ) struct (16) zero-ref ld-addr-op | |
; V02 tmp0 [V02,T07] ( 2, 4 ) int -> rax | |
; V03 tmp1 [V03,T08] ( 2, 4 ) int -> rax | |
; V04 tmp2 [V04,T04] ( 5, 5 ) int -> rax V01.<X>k__BackingField(offs=0x00) P-INDEP | |
; V05 tmp3 [V05,T05] ( 5, 5 ) int -> rsi V01.<Y>k__BackingField(offs=0x04) P-INDEP | |
; V06 tmp4 [V06,T06] ( 5, 5 ) int -> rdi V01.<Z>k__BackingField(offs=0x08) P-INDEP | |
;# V07 OutArgs [V07 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; V08 cse0 [V08,T01] ( 14, 14 ) int -> rax | |
; V09 cse1 [V09,T02] ( 10, 10 ) int -> rsi | |
; V10 cse2 [V10,T03] ( 6, 6 ) int -> rdi | |
; | |
; Lcl frame size = 0 | |
G_M42523_IG01: | |
G_M42523_IG02: | |
8B07 mov eax, dword ptr [rdi] ; copy to registers | |
815B7704 mov esi, dword ptr [rdi+4] | |
8B7F08 mov edi, dword ptr [rdi+8] ; end copy to registers | |
03C6 add eax, esi | |
03C7 add eax, edi | |
G_M42523_IG03: | |
C3 ret | |
; Total bytes of code 13, prolog size 0 for method ConsoleApp2.Program:Length2(byref):int | |
; ============================================================ | |
; Assembly listing for method ConsoleApp2.Program:Length21(byref):int | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rsp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
; V00 arg0 [V00,T00] ( 5, 5 ) byref -> rdi | |
; V01 tmp0 [V01,T01] ( 2, 4 ) int -> rax | |
; V02 tmp1 [V02,T02] ( 2, 4 ) int -> rax | |
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; | |
; Lcl frame size = 0 | |
G_M60300_IG01: | |
G_M60300_IG02: | |
8B07 mov eax, dword ptr [rdi] ; note no copy | |
034704 add eax, dword ptr [rdi+4] | |
034708 add eax, dword ptr [rdi+8] | |
G_M60300_IG03: | |
C3 ret | |
; Total bytes of code 9, prolog size 0 for method ConsoleApp2.Program:Length21(byref):int | |
; ============================================================ | |
; Assembly listing for method ConsoleApp2.Program:Length3(struct):int | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rsp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
; V00 arg0 [V00,T00] ( 5, 5 ) struct (16) [rsp+0x08] do-not-enreg[SF] ld-addr-op | |
; V01 tmp0 [V01,T01] ( 2, 4 ) int -> rax | |
; V02 tmp1 [V02,T02] ( 2, 4 ) int -> rax | |
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; | |
; Lcl frame size = 24 | |
G_M42524_IG01: | |
4883EC18 sub rsp, 24 | |
48897C2408 mov qword ptr [rsp+08H], rdi | |
89742410 mov dword ptr [rsp+10H], esi | |
G_M42524_IG02: | |
8B442408 mov eax, dword ptr [rsp+08H] | |
0344240C add eax, dword ptr [rsp+0CH] | |
03442410 add eax, dword ptr [rsp+10H] | |
G_M42524_IG03: | |
4883C418 add rsp, 24 | |
C3 ret | |
; Total bytes of code 30, prolog size 4 for method ConsoleApp2.Program:Length3(struct):int | |
; ============================================================ | |
; Assembly listing for method ConsoleApp2.Program:Length4(byref):int | |
; Emitting BLENDED_CODE for X64 CPU with AVX | |
; optimized code | |
; rsp based frame | |
; partially interruptible | |
; Final local variable assignments | |
; | |
; V00 arg0 [V00,T00] ( 5, 5 ) byref -> rdi | |
; V01 tmp0 [V01,T01] ( 2, 4 ) int -> rax | |
; V02 tmp1 [V02,T02] ( 2, 4 ) int -> rax | |
;# V03 OutArgs [V03 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] | |
; | |
; Lcl frame size = 0 | |
G_M42529_IG01: | |
G_M42529_IG02: | |
8B07 mov eax, dword ptr [rdi] ; note no copy | |
034704 add eax, dword ptr [rdi+4] | |
034708 add eax, dword ptr [rdi+8] | |
G_M42529_IG03: | |
C3 ret | |
; Total bytes of code 9, prolog size 0 for method ConsoleApp2.Program:Length4(byref):int | |
; ============================================================ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Discussion
Method with argument by value
Length1
andLength3
produce the same code. The caller makes a copy of the struct, and this copy is passed to the method. The method can assume this copy is immutable (in the sense that the copy is thrown away when the method goes out of scope), hence can operate directly on the values.Method with argument by reference
ref
Length21
doesn't need a copy, because the argument is passed by reference. The compiler can assume a mutable struct, and can operate direct on the values.in
in
means "readonly ref", so the compiler has to distinguish two cases: mutable and immutable (readonly) structs.Mutable structs
For mutable structs the compiler has to assume that the struct is mutated, which is not allowed by
in
, so it has to make a copy of the struct. This copy is not done an the callsite, but on the callee -- can be clearly seen in the IL and dasm.The code is equivalent to:
Immutable (readonly) structs
In this case the compiler knows that the struct can't be mutated, hence it can produce optimized code without the need for any copy.
Benchmarks