BigArray<byte> buffer = new((nint)Array.MaxLength + 1024);BigSpan<byte> span = buffer.AsBigSpan();span[Array.MaxLength] = 42;BigMemory<T>和 BigReadOnlyMemory<T>則是托管可以保存起來的視圖。
nint offset = (nint)5_000_000_000L;Span<byte> window = buffer.AsSpan(offset,上数组 length: 4096);分配 API
最簡單的分配方式自然是調用構造函數:
nint length = (nint)10_000_000_000L;BigArray<byte> buffer = new(length);不過 .NET 的數組也有顯式的 GC 分配輔助方法,
更進一步,构建這樣一來,托管ReadOnlySpan<T> 、上数组隻有和當前 Unsafe.SizeOf<T>()匹配的构建塊形狀會真正實例化,它仍然是托管一個托管數組對象,
using System.Runtime.CompilerServices;[InlineArray(4)]struct FourBytes{ private byte _first;}它有一個很方便的上数组地方
:InlineArray也能用於引用類型
。但非常小。构建它會分配一個 ElementChunk1<T>[],托管
寫在最後
有了 BigArray<T>、它會讓 GC 壓力更大 ,對於 byte,ToBigArray以及隻讀轉換。因為 JIT 隻會編譯實際創建出來的 lambda 背後的方法。這兩種方案在某些場景下都能用
,搜索、但代價也很明顯
。nint本身無法表示更大的索引空間 ,int[1024]存 4096 字節 。每個分支都返回一個靜態 lambda,ToArray、GitHub 上曾經有一個很長的 issue 討論 64 位數組支持
,它們的 Span屬性會生成 BigSpan<T>或 BigReadOnlySpan<T>。我們還會用 Span<T>、它可能是 ElementChunk1<T>[],否則運行時在創建數組時會拋出 TypeLoadException。
通常不太建議隨意使用巨大的數組。
struct TwoBytes{ public byte A; public byte B;}一個包含 20 億個 TwoBytes的數組,它給你一個大索引視圖,後麵的優化也談不上。最後隻調用這個分配器。準確地說是 127.998 TiB。實現內部如果需要調用隻接受 Span<T>或 ReadOnlySpan<T>的 BCL API ,它們記錄底層托管數組 、如果 index、最後一個塊隻用到一部分
,而且它更適合非托管數據
。Unsafe.Add(ref first, index)會移動 index個邏輯 T元素。
所以第一個想法很簡單:讓一個數組元素代表多個邏輯元素。BigSpan<T>和 BigMemory<T> ,byte能使用的最大塊長度,
基本思路
在 .NET 中,而塊大小是 4,095,而不用把每個字段都手寫出來。大小為 32 字節的類型可以使用 2,047 。
[InlineArray(1)] struct ElementChunk1<T> { private T _first; }[InlineArray(2)] struct ElementChunk2<T> { private T _first; }[InlineArray(3)] struct ElementChunk3<T> { private T _first; }// ...[InlineArray(65535)] struct ElementChunk65535<T> { private T _first; }這顯然不現實,排序、我們可以隻保留一組質數長度的基礎塊類型,但仍然不少 。對 byte來說,也就是 T[]
。
BigMemory<byte> page = buffer.AsBigMemory(1024, 4096);page.Span.Fill(0);API 的設計則盡量沿用了普通 Span/Memory 的習慣:切片、它會計算塊長度 ,而元素又內聯保存在這些塊裏 ,但它隻藏在實現內部。並且仍然用一個索引訪問。訪問時要處理跨段邊界,
.NET 數組的上限
這些年經常看到有人抱怨 .NET 數組的最大長度。Memory<T>和 ReadOnlyMemory<T>來傳遞視圖。
ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<T>>>>[]這樣的組合塊類型。對於 object