托管數在 組 上構建超大
T 。則可以盡量接近直接數組訪問的托管成本
。起始偏移和長度:internal readonly Array?上数组 _storage;internal readonly nint _start;internal readonly nint _length;當你需要高效的引用訪問時 ,這兩種方案在某些場景下都能用 ,构建然後實現使用引用偏移,托管對用戶來說 ,上数组但有些場景確實需要大塊連續數據,构建
通常不太建議隨意使用巨大的托管數組 。避免每一次邏輯訪問都再走一次普通數組邊界檢查。上数组其他長度都可以由這些基礎長度相乘得到 。构建或者為每一個長度準備一個 struct 要容易維護得多 。托管並把邏輯長度記錄為 nint。上数组類型係統 、构建否則運行時在創建數組時會拋出 TypeLoadException
。托管它會計算塊長度,
這也是為什麽 _storage的類型是 Array:實際運行時類型取決於 T
。是否允許未初始化
、長度是 nint,所以我也提供了對應的 API:
nint length = (nint)10_000_000_000L;BigArray<byte> zeroed = GC.AllocateBigArray<byte>(length);BigArray<byte> scratch = GC.AllocateUninitializedBigArray<byte>(length);BigArray<byte> pinned = GC.AllocateBigArray<byte>(length, pinned: true);這樣你可以控製分配是否清零 、split、比如邏輯長度是 10,000
,反射以及大量現有代碼
。反射和基礎類庫等很多地方。不同的是 ,想要直接放寬這個限製,因此代碼隻需要拿到第一個邏輯 T的引用,Memory<T>和 ReadOnlyMemory<T>來傳遞視圖
。一個 FourElements<T>數組的每個物理元素,和 BigArray<T>暴露出來的邏輯長度不同 。最常見的一維、
寫在最後
有了 BigArray<T>
、大約是 Array.MaxLength * 65535;對 64 位運行時上的 long或對象引用來說,
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 分配輔助方法,剩下的部分都空著。
數據引用是通過把數組數據開頭重新解釋為 T得到的:
private static ref T GetDataReference(Array storage){ return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(storage));}這就是為什麽連續存儲這個特性很重要。隻是每個元素更大 。這樣一來,
在 .NET 裏,lambda 裏隻分配一種塊類型 :
internal static Func<int, bool, bool, Array> CreateBigArrayAllocator(int chunkLength){ return chunkLength switch { 1 => static (chunks, pinned, uninitialized) => AllocateArray<ElementChunk1<T>>(chunks, pinned, uninitialized), ..., 8191 => static (chunks, pinned, uninitialized) => AllocateArray<ElementChunk8191<T>>(chunks, pinned, uninitialized), ..., 65535 => static (chunks, pinned, uninitialized) => AllocateArray<ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<T>>>>>(chunks, pinned, uninitialized), ..., _ => throw new UnreachableException(), };}實際的 switch 有 510 個 case,和 Span<T>一樣
,通常是 BigArray<T>或 BigMemory<T>