在.Net 8中转ImmutableArray性能优化
前言
.Net 8的预览版不知不觉已经发布5,这个月要发布PreView 6了,在往后的版本就开始转为RC版本,会暂停新特性或者新功能代码,有新特性的话,会加入到下一个大版本中.下面进入正文.数组在转ImmutableArray在.Net 8增加新的方式,这种方式性能更好.当如,如果对性能要求不高的话,可以在数组中直接使用ToImmutableArray转换为ImmutableArray.测试代码
namespace CSharpBenchmarks.ArrayTest
{
[MemoryDiagnoser]
[DisassemblyDiagnoser(printSource: true)]
public class ImmutableArrayTest
{
public People[] p1;
[Params(100, 1000)]
public int Count { get; set; }
[GlobalSetup]
public void Setup()
{
p1 = new People[Count];
for (int i = 0; i < Count; i++)
{
p1[i] = new People { Id = i+1, Name = (i+1).ToString() };
}
}
[Benchmark]
public ImmutableArray<People> NewImmutableArrayTest1()
{
return p1.ToImmutableArray();
}
[Benchmark]
public ImmutableArray<People> NewImmutableArrayTest2()
{
return Unsafe.As<People[], ImmutableArray<People>>(ref p1);
}
#if NET8_0_OR_GREATER
[Benchmark]
public ImmutableArray<People> NewImmutableArrayTest3()
{
return ImmutableCollectionsMarshal.AsImmutableArray(p1);
}
#endif
[GlobalCleanup]
public void Cleanup()
{
p1 = null;
}
}
}
使用BenchmarkDotNet进行性能基准测试,看看那个性能会好一些呢?
如果你需要性能的话,在.Net 8 你可以使用ImmutableCollectionsMarshal(.Net 8新增)中AsImmutableArray,将数组转换不可变数组.在其他版本中,可以Unsafe.As转换不可变数组.不太注重性能的话,可以依然使用ToImmutableArray.
下面我们分别看一些生成的IL代码:
.method public hidebysig
instance valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<class CSharpBenchmarks.ArrayTest.People> NewImmutableArrayTest1 () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 25 00 00 00 4a 46 3a 5c 67 69 74 63 6f 64
65 5c 4d 79 4c 65 61 72 6e 69 6e 67 5c 73 72 63
5c 43 53 68 61 72 70 42 65 6e 63 68 6d 61 72 6b
73 5c 41 72 72 61 79 54 65 73 74 5c 49 6d 6d 75
74 61 62 6c 65 41 72 72 61 79 54 65 73 74 2e 63
73 00 00
)
// Method begins at RVA 0x304a
// Header size: 1
// Code size: 12 (0xc)
.maxstack 8
// return p1.ToImmutableArray();
IL_0000: ldarg.0
IL_0001: ldfld class CSharpBenchmarks.ArrayTest.People[] CSharpBenchmarks.ArrayTest.ImmutableArrayTest::p1
IL_0006: call valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<!!0> [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray::ToImmutableArray<class CSharpBenchmarks.ArrayTest.People>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>)
IL_000b: ret
} // end of method ImmutableArrayTest::NewImmutableArrayTest1
.method public hidebysig
instance valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<class CSharpBenchmarks.ArrayTest.People> NewImmutableArrayTest2 () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 2b 00 00 00 4a 46 3a 5c 67 69 74 63 6f 64
65 5c 4d 79 4c 65 61 72 6e 69 6e 67 5c 73 72 63
5c 43 53 68 61 72 70 42 65 6e 63 68 6d 61 72 6b
73 5c 41 72 72 61 79 54 65 73 74 5c 49 6d 6d 75
74 61 62 6c 65 41 72 72 61 79 54 65 73 74 2e 63
73 00 00
)
// Method begins at RVA 0x3057
// Header size: 1
// Code size: 17 (0x11)
.maxstack 8
// return Unsafe.As<People[], ImmutableArray<People>>(ref p1);
IL_0000: ldarg.0
IL_0001: ldflda class CSharpBenchmarks.ArrayTest.People[] CSharpBenchmarks.ArrayTest.ImmutableArrayTest::p1
IL_0006: call !!1& [System.Runtime]System.Runtime.CompilerServices.Unsafe::As<class CSharpBenchmarks.ArrayTest.People[], valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<class CSharpBenchmarks.ArrayTest.People>>(!!0&)
IL_000b: ldobj valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<class CSharpBenchmarks.ArrayTest.People>
IL_0010: ret
} // end of method ImmutableArrayTest::NewImmutableArrayTest2
.method public hidebysig
instance valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<class CSharpBenchmarks.ArrayTest.People> NewImmutableArrayTest3 () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 32 00 00 00 4a 46 3a 5c 67 69 74 63 6f 64
65 5c 4d 79 4c 65 61 72 6e 69 6e 67 5c 73 72 63
5c 43 53 68 61 72 70 42 65 6e 63 68 6d 61 72 6b
73 5c 41 72 72 61 79 54 65 73 74 5c 49 6d 6d 75
74 61 62 6c 65 41 72 72 61 79 54 65 73 74 2e 63
73 00 00
)
// Method begins at RVA 0x3069
// Header size: 1
// Code size: 12 (0xc)
.maxstack 8
// return ImmutableCollectionsMarshal.AsImmutableArray<People>(p1);
IL_0000: ldarg.0
IL_0001: ldfld class CSharpBenchmarks.ArrayTest.People[] CSharpBenchmarks.ArrayTest.ImmutableArrayTest::p1
IL_0006: call valuetype [System.Collections.Immutable]System.Collections.Immutable.ImmutableArray`1<!!0> [System.Collections.Immutable]System.Runtime.InteropServices.ImmutableCollectionsMarshal::AsImmutableArray<class CSharpBenchmarks.ArrayTest.People>(!!0[])
IL_000b: ret
} // end of method ImmutableArrayTest::NewImmutableArrayTest3
在IL层面上,NewImmutableArrayTest2和NewImmutableArrayTest3代码还是有差异的.接着我们看一下汇编代码:
; CSharpBenchmarks.ArrayTest.ImmutableArrayTest.NewImmutableArrayTest2()
; return Unsafe.As<People[], ImmutableArray<People>>(ref p1);
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
mov rax,[rcx+8]
ret
; Total bytes of code 5
; CSharpBenchmarks.ArrayTest.ImmutableArrayTest.NewImmutableArrayTest3()
; return ImmutableCollectionsMarshal.AsImmutableArray(p1);
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
mov rax,[rcx+8]
ret
; Total bytes of code 5
这里就不贴NewImmutableArrayTest1的汇编代码,主要太长了,发现NewImmutableArrayTest2和NewImmutableArrayTest3最终生成的汇编代码是一样.
秋风
2023-07-02