ネコのために鐘は鳴る

寺院に住み着くパソコ〇好き

(C#) メモリ確保ベンチマーク on .NET6

この記事は .NET6 Advent Calendar 2021 の14日目の記事です。

.NET6 におけるバッファメモリ確保

以前 .NET Framework4.8 でメモリ確保の方法についてベンチマークを取り、記事にしたことがあります。 (参考 (C#) メモリ確保ベンチマーク 6種盛り)

.NET6 でいろいろとバージョンアップしているので、.NET6 でベンチマークを取ってみました。 前回は6通りの方法を試しましたが、今回は新たに追加されたメソッドを含めて7通りを試します。

ベンチマークライブラリはいつも通り BenchmarkDotNet を使用します。

// (1)
[Benchmark(Baseline = true, Description = "new byte[]")]
public byte[] NewAlloc()
{
    return new byte[Size];
}

// (2)
[Benchmark(Description = "ArrayPool")]
public void SharedPool()
{
    var array = ArrayPool<byte>.Shared.Rent(Size);
    ArrayPool<byte>.Shared.Return(array);
}

// (3)
[Benchmark(Description = "Marshal.Alloc")]
public void MarshalAlloc()
{
    var array = Marshal.AllocHGlobal(Size);
    Marshal.FreeHGlobal(array);
}

// (4)
[Benchmark(Description = "Marshal.Alloc + GCPressure")]
public void MarshalAlloc_WithGCPressure()
{
    var array = Marshal.AllocHGlobal(Size);
    GC.AddMemoryPressure(Size);
    Marshal.FreeHGlobal(array);
    GC.RemoveMemoryPressure(Size);
}

// (5)
[Benchmark(Description = "NativeMemory.Alloc")]
public unsafe void NativeAlloc()
{
    var array = NativeMemory.Alloc((nuint)Size);
    NativeMemory.Free(array);
}

// (6)
[Benchmark(Description = "NativeMemory.Alloc + GCPressure")]
public unsafe void NativeAlloc_WithGCPressure()
{
    var array = NativeMemory.Alloc((nuint)Size);
    GC.AddMemoryPressure(Size);
    NativeMemory.Free(array);
    GC.RemoveMemoryPressure(Size);
}

// (7)
[Benchmark(Description = "GC.AllocateUninitializedArray")]
public byte[] UninitializedArray()
{
    return GC.AllocateUninitializedArray<byte>(Size);
}

メモリ確保の方法として、それぞれ

  • (1) new byte[N]
  • (2) ArrayPool<byte>.Shared.Rent(N)
  • (3) Marshal.AllocHGlobal(N)
  • (4) (3) + GC.AddMemoryPressure
  • (5) NativeMemory.Alloc(N)
  • (6) (5) + GC.AddMemoryPressure
  • (7) GC.AllocateUninitializedArray<byte>(N)

です。(1), (2), (7) はマネージドメモリ、(3), (4), (5), (6) はアンマネージドメモリです。

ベンチマーク結果

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19042.1415 (20H2/October2020Update)
Intel Core i7-10700 CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.100
  [Host]    : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT
  RyuJitX64 : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT

Job=RyuJitX64  Jit=RyuJit  Platform=X64  

f:id:ikorin2:20211220031013p:plain

もっとも単純な (1) の new byte[N] は、サイズに対してほぼ比例です。

また、(2) の ArrayPool<byte>.Shared.Rent(N) はサイズによらず定数です。これは、ArrayPool<byte> の内部プールが定常状態で配列がプールから取り出される場合、N によらず常に同じフローです。 ArrayPool.Shared は .NET6 で内部実装に変更がありました。.NET5 以前ではスレッドごとに独立に、長さの異なる17個の配列がキープされていました。.NET5 以前の実装の解説についてはこちらを参照。 これが .NET6 でキープされる配列の最大長が撤廃され、長さが220以上の配列を借りてもプールされます。また、T が unmanaged 型の場合、内部で新たに配列を作るときに new T[] ではなく GC.AllocateUninitializedArray<byte>(N) を使用するようになりました。

(3) と (5) はともにアンマネージドメモリを確保しています。 (5) について、以前の .NET Framework4.8 でのベンチマークでは自分でC++std::mallocstd::free をする dll を用意して呼び出していましたが、.NET6 から同様のメソッドが標準ライブラリ入りしたためそちらを使用しています。また、.NET6 で NativeMemory.Alloc(N) が実装されたことによって (3) の Marshal.AllocHGlobal(N) も内部で同じものを呼ぶように変更されました。したがって、(3) と (5) は全く同一でグラフも完全に一致しています。

(4) と (6) は (3) と (5) に GC Pressure の操作を追加したもので、その操作にかかる時間だけ時間が多くかかっています。

(7) は .NET6 から新たに追加されたメソッドです。new byte[N] と同じくマネージドメモリを確保しますが、配列の要素はゼロ初期化されていません。その分だけ (1) よりも速くなっています。ここで注目すべきは、2048 以下の時には (1) とほぼ一致していることです。これは、GC.AllocateUninitializedArray<T>(N) はサイズが2048バイト未満の場合は内部で new T[N] を呼び出しており、実装上の都合でその方が速いためとのことです。このメソッドの T は unmanaged 型、つまりプリミティブ型と、再帰的に全てのフィールドがプリミティブ型で構成される構造体でのみ使用可能です。

補足など

GC Pressure について

GC.AddMemoryPressure については私自身、使いどころがうまく理解できていません。確かにアンマネージドメモリを確保するとメモリ使用量は増えたにもかかわらずランタイムには認知されません。この時、メモリが逼迫するとランタイムはメモリを空けようと GC を走らせますが、実際にはメモリは空かず、逼迫した状態が続くので何度も意味のない GC を連発するようなことが起こるのかもしれません。しかし、もともとネイティブな外部 dll を呼び出して相互運用するようなアプリケーションの場合、ネイティブ側でどれだけメモリが確保されているかなど C# 側からは知る由もなく、GC.AddMemoryPressure を使うことはできません。これは私の勝手な判断ですが、合計でギガバイト以上の大きなメモリを確保する場合などのメモリが逼迫しうる量を確保しない限り、GC.AddMemoryPressure を使う必要はないと思っています。(もし GC.AddMemoryPressure を使うべき明確な理由や場面を知っている方がいれば教えてください)

アンマネージドメモリの使用について

アンマネージドメモリはその名の通りランタイムに管理されていません。利点として GC の対象にならないため停止時間に影響を与えません。一方欠点として解放を忘れるとメモリリークします。C++ や Rust のようにメモリリークを言語レベルで防ぐためのサポートもなく、生のポインタです。一応 IDisposable に包むと using 構文で自動解放できますが、using を強制する言語サポートはないため危険度としては同じです。ファイナライザを持つクラスに包むと最後のセーフティーとしてメモリリークはしませんが、GC の停止時間中に解放を行うため停止時間を削減するという目的そのものが無意味になります。ライブラリの中で完結する一時バッファとして実装者が責任を持てる範囲以外では使用すべきでないと思います。

また、GC を持つ C# が Rust や C++ より優れている部分として、メモリの断片化が発生しずらいという利点があります。アンマネージドメモリを多用するとこの利点をつぶします。小規模なメモリを何度もアンマネージドメモリに確保し、さらにそれらが長期間生存するような場面には向きません。

バッファとしての使用は、特別な理由がない限り ArrayPool<byte>.Shared.Rent(N) を使用すべきでしょう。

[追記]

.NET Framework 4.8 のときの記事とは計測したマシンスペックが異なるため、以前の記事と単純な実行時間での比較はしないでください。あくまで各手法間の比率で見てください。

ベンチマーク結果の詳細

Method Size Mean Error StdDev Median Ratio RatioSD
'new byte' 16 2.420 ns 0.0573 ns 0.0536 ns 2.418 ns 1.00
ArrayPool 16 14.197 ns 0.0241 ns 0.0214 ns 14.191 ns 5.87 0.14
Marshal.Alloc 16 49.691 ns 0.0782 ns 0.0694 ns 49.684 ns 20.55 0.46
'Marshal.Alloc + GCPressure' 16 87.830 ns 0.1201 ns 0.1124 ns 87.784 ns 36.30
NativeMemory.Alloc 16 49.087 ns 0.0693 ns 0.0579 ns 49.084 ns 20.34 0.47
'NativeMemory.Alloc + GCPressure' 16 86.639 ns 0.1773 ns 0.1572 ns 86.568 ns 35.84
GC.AllocateUninitializedArray 16 1.896 ns 0.0481 ns 0.0450 ns 1.882 ns 0.78 0.03
'new byte' 32 2.486 ns 0.0307 ns 0.0287 ns 2.487 ns 1.00
ArrayPool 32 15.458 ns 0.0095 ns 0.0074 ns 15.456 ns 6.21 0.07
Marshal.Alloc 32 49.709 ns 0.1392 ns 0.1302 ns 49.690 ns 19.99 0.24
'Marshal.Alloc + GCPressure' 32 87.716 ns 0.0756 ns 0.0632 ns 87.705 ns 35.28
NativeMemory.Alloc 32 47.426 ns 0.0657 ns 0.0582 ns 47.415 ns 19.08 0.23
'NativeMemory.Alloc + GCPressure' 32 85.087 ns 0.2800 ns 0.2619 ns 84.965 ns 34.22
GC.AllocateUninitializedArray 32 2.411 ns 0.0358 ns 0.0334 ns 2.405 ns 0.97 0.02
'new byte' 64 3.417 ns 0.0667 ns 0.0624 ns 3.436 ns 1.00
ArrayPool 64 14.707 ns 0.0244 ns 0.0217 ns 14.699 ns 4.30 0.08
Marshal.Alloc 64 53.475 ns 0.0520 ns 0.0434 ns 53.472 ns 15.63 0.29
'Marshal.Alloc + GCPressure' 64 89.940 ns 0.0665 ns 0.0589 ns 89.923 ns 26.28
NativeMemory.Alloc 64 46.917 ns 0.1785 ns 0.1670 ns 46.901 ns 13.73 0.23
'NativeMemory.Alloc + GCPressure' 64 85.983 ns 0.0746 ns 0.0582 ns 85.990 ns 25.15
GC.AllocateUninitializedArray 64 3.381 ns 0.0779 ns 0.0728 ns 3.389 ns 0.99 0.03
'new byte' 128 5.155 ns 0.0990 ns 0.0926 ns 5.165 ns 1.00
ArrayPool 128 15.444 ns 0.0326 ns 0.0305 ns 15.426 ns 3.00 0.05
Marshal.Alloc 128 50.599 ns 0.0346 ns 0.0270 ns 50.603 ns 9.80 0.19
'Marshal.Alloc + GCPressure' 128 89.218 ns 0.0967 ns 0.0905 ns 89.204 ns 17.31
NativeMemory.Alloc 128 47.705 ns 0.1220 ns 0.1081 ns 47.667 ns 9.25 0.17
'NativeMemory.Alloc + GCPressure' 128 89.048 ns 0.0671 ns 0.0628 ns 89.063 ns 17.28
GC.AllocateUninitializedArray 128 5.120 ns 0.0627 ns 0.0489 ns 5.142 ns 0.99 0.02
'new byte' 256 8.876 ns 0.1177 ns 0.1101 ns 8.898 ns 1.00
ArrayPool 256 14.331 ns 0.2157 ns 0.1912 ns 14.230 ns 1.61 0.03
Marshal.Alloc 256 50.962 ns 0.3189 ns 0.2983 ns 50.958 ns 5.74 0.07
'Marshal.Alloc + GCPressure' 256 91.727 ns 0.7887 ns 0.7378 ns 91.540 ns 10.34
NativeMemory.Alloc 256 47.855 ns 0.1256 ns 0.1175 ns 47.818 ns 5.39 0.07
'NativeMemory.Alloc + GCPressure' 256 88.592 ns 0.0957 ns 0.0848 ns 88.622 ns 9.98
GC.AllocateUninitializedArray 256 8.880 ns 0.1124 ns 0.1051 ns 8.934 ns 1.00 0.02
'new byte' 512 16.057 ns 0.3431 ns 0.3210 ns 15.960 ns 1.00
ArrayPool 512 14.185 ns 0.0184 ns 0.0172 ns 14.177 ns 0.88 0.02
Marshal.Alloc 512 50.141 ns 0.0323 ns 0.0270 ns 50.139 ns 3.13 0.07
'Marshal.Alloc + GCPressure' 512 100.263 ns 0.0938 ns 0.0831 ns 100.267 ns 6.25
NativeMemory.Alloc 512 48.371 ns 0.0636 ns 0.0595 ns 48.379 ns 3.01 0.06
'NativeMemory.Alloc + GCPressure' 512 96.198 ns 0.0923 ns 0.0863 ns 96.201 ns 5.99
GC.AllocateUninitializedArray 512 16.169 ns 0.2531 ns 0.2367 ns 16.236 ns 1.01 0.02
'new byte' 1024 30.097 ns 0.4235 ns 0.3961 ns 30.006 ns 1.00
ArrayPool 1024 14.679 ns 0.0093 ns 0.0073 ns 14.679 ns 0.49 0.01
Marshal.Alloc 1024 50.266 ns 0.0349 ns 0.0309 ns 50.269 ns 1.67 0.02
'Marshal.Alloc + GCPressure' 1024 108.525 ns 0.0833 ns 0.0739 ns 108.522 ns 3.61
NativeMemory.Alloc 1024 47.416 ns 0.0289 ns 0.0270 ns 47.405 ns 1.58 0.02
'NativeMemory.Alloc + GCPressure' 1024 110.094 ns 0.0874 ns 0.0818 ns 110.103 ns 3.66
GC.AllocateUninitializedArray 1024 29.920 ns 0.6143 ns 0.5746 ns 29.897 ns 0.99 0.03
'new byte' 2048 57.893 ns 0.9656 ns 0.8560 ns 57.662 ns 1.00
ArrayPool 2048 14.776 ns 0.0128 ns 0.0113 ns 14.773 ns 0.26 0.00
Marshal.Alloc 2048 51.905 ns 0.0445 ns 0.0395 ns 51.899 ns 0.90 0.01
'Marshal.Alloc + GCPressure' 2048 114.753 ns 0.1050 ns 0.0982 ns 114.727 ns 1.98
NativeMemory.Alloc 2048 48.999 ns 0.0228 ns 0.0202 ns 49.002 ns 0.85 0.01
'NativeMemory.Alloc + GCPressure' 2048 111.208 ns 0.1044 ns 0.0872 ns 111.214 ns 1.92
GC.AllocateUninitializedArray 2048 58.826 ns 0.1744 ns 0.1456 ns 58.761 ns 1.02 0.02
'new byte' 4096 118.348 ns 2.3840 ns 2.5508 ns 117.820 ns 1.00
ArrayPool 4096 14.677 ns 0.0147 ns 0.0138 ns 14.673 ns 0.12 0.00
Marshal.Alloc 4096 50.449 ns 0.0249 ns 0.0233 ns 50.445 ns 0.43 0.01
'Marshal.Alloc + GCPressure' 4096 120.027 ns 0.0595 ns 0.0465 ns 120.024 ns 1.02
NativeMemory.Alloc 4096 47.249 ns 0.0265 ns 0.0222 ns 47.247 ns 0.40 0.01
'NativeMemory.Alloc + GCPressure' 4096 115.660 ns 0.1309 ns 0.1160 ns 115.620 ns 0.98
GC.AllocateUninitializedArray 4096 68.978 ns 0.1841 ns 0.1537 ns 68.998 ns 0.59 0.01
'new byte' 8192 241.119 ns 4.5964 ns 4.2995 ns 242.919 ns 1.00
ArrayPool 8192 18.240 ns 0.0137 ns 0.0114 ns 18.235 ns 0.08 0.00
Marshal.Alloc 8192 50.385 ns 0.0422 ns 0.0374 ns 50.369 ns 0.21 0.00
'Marshal.Alloc + GCPressure' 8192 120.433 ns 0.1308 ns 0.1223 ns 120.399 ns 0.50
NativeMemory.Alloc 8192 47.494 ns 0.0318 ns 0.0266 ns 47.487 ns 0.20 0.00
'NativeMemory.Alloc + GCPressure' 8192 120.283 ns 0.0880 ns 0.0735 ns 120.262 ns 0.50
GC.AllocateUninitializedArray 8192 74.431 ns 0.1530 ns 0.1277 ns 74.431 ns 0.31 0.01
'new byte' 16384 424.195 ns 8.2143 ns 8.7893 ns 421.584 ns 1.00
ArrayPool 16384 17.953 ns 0.0227 ns 0.0201 ns 17.943 ns 0.04 0.00
Marshal.Alloc 16384 53.502 ns 0.2266 ns 0.2119 ns 53.448 ns 0.13 0.00
'Marshal.Alloc + GCPressure' 16384 120.846 ns 0.1743 ns 0.1456 ns 120.775 ns 0.28
NativeMemory.Alloc 16384 47.405 ns 0.0418 ns 0.0371 ns 47.391 ns 0.11 0.00
'NativeMemory.Alloc + GCPressure' 16384 117.896 ns 0.0476 ns 0.0371 ns 117.901 ns 0.28
GC.AllocateUninitializedArray 16384 94.471 ns 0.4015 ns 0.3353 ns 94.554 ns 0.22 0.00
'new byte' 32768 790.026 ns 15.5811 ns 15.3027 ns 796.038 ns 1.00
ArrayPool 32768 14.225 ns 0.0072 ns 0.0060 ns 14.222 ns 0.02 0.00
Marshal.Alloc 32768 137.537 ns 0.3049 ns 0.2703 ns 137.594 ns 0.17 0.00
'Marshal.Alloc + GCPressure' 32768 222.805 ns 0.1789 ns 0.1673 ns 222.784 ns 0.28
NativeMemory.Alloc 32768 139.468 ns 2.7590 ns 3.8677 ns 140.092 ns 0.17 0.01
'NativeMemory.Alloc + GCPressure' 32768 216.506 ns 1.1494 ns 1.0751 ns 215.987 ns 0.27
GC.AllocateUninitializedArray 32768 137.147 ns 0.2657 ns 0.2356 ns 137.163 ns 0.17 0.00
'new byte' 65536 1,523.391 ns 27.7315 ns 23.1571 ns 1,532.096 ns 1.00
ArrayPool 65536 18.432 ns 0.0124 ns 0.0104 ns 18.428 ns 0.01 0.00
Marshal.Alloc 65536 109.773 ns 2.2175 ns 3.9986 ns 111.761 ns 0.07 0.00
'Marshal.Alloc + GCPressure' 65536 189.097 ns 3.6164 ns 3.2059 ns 190.010 ns 0.12
NativeMemory.Alloc 65536 105.710 ns 1.4795 ns 2.7423 ns 106.750 ns 0.07 0.00
'NativeMemory.Alloc + GCPressure' 65536 185.469 ns 0.3173 ns 0.2812 ns 185.429 ns 0.12
GC.AllocateUninitializedArray 65536 237.496 ns 1.2092 ns 1.0719 ns 237.694 ns 0.16 0.00
'new byte' 131072 4,932.622 ns 18.8168 ns 15.7129 ns 4,933.810 ns 1.000
ArrayPool 131072 16.016 ns 0.0116 ns 0.0103 ns 16.010 ns 0.003 0.00
Marshal.Alloc 131072 119.555 ns 2.4092 ns 3.6059 ns 118.869 ns 0.025 0.00
'Marshal.Alloc + GCPressure' 131072 241.775 ns 4.8540 ns 12.0883 ns 246.642 ns 0.049
NativeMemory.Alloc 131072 112.695 ns 2.2500 ns 4.1142 ns 112.978 ns 0.023 0.00
'NativeMemory.Alloc + GCPressure' 131072 203.887 ns 3.4076 ns 3.1875 ns 204.198 ns 0.041
GC.AllocateUninitializedArray 131072 2,482.482 ns 8.6712 ns 7.6868 ns 2,482.735 ns 0.503 0.00
'new byte' 262144 9,745.615 ns 14.6181 ns 13.6738 ns 9,744.838 ns 1.000
ArrayPool 262144 14.571 ns 0.0162 ns 0.0151 ns 14.564 ns 0.001 0.00
Marshal.Alloc 262144 384.751 ns 26.3435 ns 76.8453 ns 409.966 ns 0.027 0.01
'Marshal.Alloc + GCPressure' 262144 472.301 ns 13.4946 ns 37.6177 ns 479.022 ns 0.045
NativeMemory.Alloc 262144 711.295 ns 76.9815 ns 217.1276 ns 796.491 ns 0.028 0.02
'NativeMemory.Alloc + GCPressure' 262144 470.812 ns 4.2143 ns 3.9421 ns 470.417 ns 0.048
GC.AllocateUninitializedArray 262144 5,097.969 ns 55.8836 ns 52.2736 ns 5,096.607 ns 0.523 0.01
'new byte' 524288 19,023.374 ns 144.1437 ns 134.8321 ns 19,030.304 ns 1.000
ArrayPool 524288 16.372 ns 0.0065 ns 0.0051 ns 16.373 ns 0.001 0.00
Marshal.Alloc 524288 864.512 ns 10.9232 ns 9.6832 ns 866.815 ns 0.045 0.00
'Marshal.Alloc + GCPressure' 524288 662.507 ns 12.7071 ns 15.1269 ns 662.596 ns 0.035
NativeMemory.Alloc 524288 660.036 ns 5.1624 ns 8.3363 ns 657.784 ns 0.035 0.00
'NativeMemory.Alloc + GCPressure' 524288 645.454 ns 12.7841 ns 14.2094 ns 650.756 ns 0.034
GC.AllocateUninitializedArray 524288 9,936.610 ns 42.6100 ns 39.8574 ns 9,959.137 ns 0.522 0.00
'new byte' 1048576 210,343.268 ns 4,036.2318 ns 3,775.4937 ns 209,505.695 ns 1.000
ArrayPool 1048576 14.407 ns 0.0054 ns 0.0045 ns 14.407 ns 0.000 0.00
Marshal.Alloc 1048576 5,664.862 ns 20.7977 ns 19.4542 ns 5,661.308 ns 0.027 0.00
'Marshal.Alloc + GCPressure' 1048576 7,513.813 ns 19.9332 ns 16.6451 ns 7,516.030 ns 0.036
NativeMemory.Alloc 1048576 4,355.013 ns 8.0762 ns 7.1594 ns 4,357.634 ns 0.021 0.00
'NativeMemory.Alloc + GCPressure' 1048576 5,020.571 ns 30.2852 ns 28.3288 ns 5,023.151 ns 0.024
GC.AllocateUninitializedArray 1048576 7,733.933 ns 129.8119 ns 121.4262 ns 7,717.241 ns 0.037 0.00
'new byte' 2097152 487,347.627 ns 61,547.5253 ns 181,474.2670 ns 576,913.638 ns 1.000
ArrayPool 2097152 15.909 ns 0.0759 ns 0.0673 ns 15.882 ns 0.000 0.00
Marshal.Alloc 2097152 4,588.926 ns 35.5412 ns 33.2452 ns 4,573.381 ns 0.010 0.01
'Marshal.Alloc + GCPressure' 2097152 8,018.549 ns 47.9033 ns 42.4650 ns 8,010.034 ns 0.018
NativeMemory.Alloc 2097152 4,589.413 ns 24.3240 ns 22.7527 ns 4,588.242 ns 0.010 0.01
'NativeMemory.Alloc + GCPressure' 2097152 7,800.359 ns 24.4334 ns 21.6596 ns 7,801.991 ns 0.018
GC.AllocateUninitializedArray 2097152 16,230.549 ns 1,001.2648 ns 2,840.4215 ns 14,941.833 ns 0.063 0.10
'new byte' 4194304 1,153,983.587 ns 22,865.1377 ns 34,917.4802 ns 1,170,402.966 ns 1.000
ArrayPool 4194304 15.194 ns 0.0714 ns 0.0668 ns 15.204 ns 0.000 0.00
Marshal.Alloc 4194304 4,695.135 ns 49.0898 ns 45.9186 ns 4,692.053 ns 0.004 0.00
'Marshal.Alloc + GCPressure' 4194304 5,120.850 ns 14.4461 ns 12.8061 ns 5,120.153 ns 0.004
NativeMemory.Alloc 4194304 7,144.996 ns 53.5635 ns 50.1033 ns 7,139.875 ns 0.006 0.00
'NativeMemory.Alloc + GCPressure' 4194304 8,005.085 ns 74.1182 ns 65.7039 ns 8,014.642 ns 0.007
GC.AllocateUninitializedArray 4194304 56,849.782 ns 9,976.5391 ns 28,943.7489 ns 48,485.974 ns 0.049 0.02
'new byte' 8388608 1,148,999.833 ns 22,853.2166 ns 40,025.5654 ns 1,149,472.656 ns 1.000
ArrayPool 8388608 14.672 ns 0.1140 ns 0.1067 ns 14.654 ns 0.000 0.00
Marshal.Alloc 8388608 4,657.386 ns 34.4025 ns 32.1801 ns 4,665.833 ns 0.004 0.00
'Marshal.Alloc + GCPressure' 8388608 5,151.209 ns 38.3535 ns 35.8759 ns 5,146.686 ns 0.005
NativeMemory.Alloc 8388608 7,180.269 ns 25.9670 ns 24.2896 ns 7,178.865 ns 0.006 0.00
'NativeMemory.Alloc + GCPressure' 8388608 5,175.859 ns 34.8045 ns 32.5561 ns 5,166.141 ns 0.005
GC.AllocateUninitializedArray 8388608 582,251.110 ns 31,505.5869 ns 92,894.9338 ns 589,878.369 ns 0.496 0.08
'new byte' 16777216 1,835,476.538 ns 36,402.7929 ns 102,674.7232 ns 1,837,862.939 ns 1.000
ArrayPool 16777216 14.438 ns 0.0617 ns 0.0577 ns 14.430 ns 0.000 0.00
Marshal.Alloc 16777216 4,766.127 ns 28.3081 ns 26.4794 ns 4,761.963 ns 0.002 0.00
'Marshal.Alloc + GCPressure' 16777216 5,266.717 ns 37.5639 ns 35.1373 ns 5,258.134 ns 0.003
NativeMemory.Alloc 16777216 7,130.423 ns 44.2171 ns 41.3607 ns 7,136.340 ns 0.004 0.00
'NativeMemory.Alloc + GCPressure' 16777216 5,322.742 ns 17.3828 ns 13.5714 ns 5,327.187 ns 0.003
GC.AllocateUninitializedArray 16777216 1,608,216.958 ns 47,482.1191 ns 140,002.0994 ns 1,607,935.217 ns 0.881 0.09
'new byte' 33554432 2,586,667.734 ns 322,461.3751 ns 950,784.6401 ns 2,520,573.242 ns 1.000
ArrayPool 33554432 17.521 ns 0.0986 ns 0.0874 ns 17.499 ns 0.000 0.00
Marshal.Alloc 33554432 7,403.940 ns 43.5760 ns 40.7610 ns 7,395.505 ns 0.003 0.00
'Marshal.Alloc + GCPressure' 33554432 5,395.639 ns 47.5055 ns 44.4367 ns 5,401.366 ns 0.003
NativeMemory.Alloc 33554432 4,674.743 ns 30.5416 ns 28.5687 ns 4,670.855 ns 0.002 0.00
'NativeMemory.Alloc + GCPressure' 33554432 5,395.462 ns 43.5393 ns 40.7267 ns 5,394.284 ns 0.003
GC.AllocateUninitializedArray 33554432 2,179,107.702 ns 43,172.6520 ns 100,059.1858 ns 2,189,359.583 ns 1.003 0.41
'new byte' 67108864 2,239,400.302 ns 139,427.1431 ns 411,104.0771 ns 2,200,031.348 ns 1.000
ArrayPool 67108864 16.233 ns 0.0554 ns 0.0491 ns 16.209 ns 0.000 0.00
Marshal.Alloc 67108864 5,134.274 ns 22.7777 ns 21.3063 ns 5,134.368 ns 0.002 0.00
'Marshal.Alloc + GCPressure' 67108864 5,696.211 ns 51.0449 ns 47.7474 ns 5,696.205 ns 0.003
NativeMemory.Alloc 67108864 7,734.205 ns 50.1103 ns 44.4215 ns 7,739.951 ns 0.004 0.00
'NativeMemory.Alloc + GCPressure' 67108864 7,179.701 ns 59.8916 ns 53.0924 ns 7,187.198 ns 0.003
GC.AllocateUninitializedArray 67108864 2,620,094.243 ns 122,614.2257 ns 361,530.8110 ns 2,662,540.283 ns 1.208 0.28
'new byte' 134217728 2,807,654.795 ns 59,852.9969 ns 174,594.0012 ns 2,787,194.434 ns 1.000
ArrayPool 134217728 17.360 ns 0.0999 ns 0.0934 ns 17.349 ns 0.000 0.00
Marshal.Alloc 134217728 8,191.272 ns 35.0218 ns 31.0459 ns 8,196.585 ns 0.003 0.00
'Marshal.Alloc + GCPressure' 134217728 6,022.638 ns 96.8344 ns 90.5789 ns 6,016.302 ns 0.002
NativeMemory.Alloc 134217728 5,672.151 ns 41.0983 ns 38.4434 ns 5,676.926 ns 0.002 0.00
'NativeMemory.Alloc + GCPressure' 134217728 6,060.486 ns 35.3757 ns 31.3597 ns 6,058.861 ns 0.002
GC.AllocateUninitializedArray 134217728 2,908,906.631 ns 57,655.1371 ns 164,493.3504 ns 2,924,241.309 ns 1.040 0.09