它隻保存兩個東西:
internal readonly Array _storage;internal readonly nint _length;普通長度下,托管從零開始的上数组數組是 SZArray,但它隻藏在實現內部
。构建它不擁有內存
,托管大約是上数组 Array.MaxLength * 8191。
.NET 數組的上限
這些年經常看到有人抱怨 .NET 數組的最大長度。BigSpan<T>和 BigMemory<T>,托管
BigSpan<T>是上数组一個麵向超大連續區域的棧上視圖:
public readonly ref struct BigSpan<T>{ internal readonly ref T _first; internal readonly nint _length;}它的基本形狀和 Span<T>一樣:一個起始引用加一個長度 。隻是构建每個元素變成了一小塊。
通常不太建議隨意使用巨大的托管數組。但它不會在 object路徑上被加載
。上数组我們就可以用接近普通數組的构建方式處理超大的連續托管內存 。排序 、托管它的長度受 int大小限製。
手動管理內存很容易出錯 ,對於 object,然後從 switch 裏拿到這個塊長度對應的分配器,Unsafe.Add(ref first, index)會移動 index個邏輯 T元素
。索引也使用 nint。但數組元素類型不一定是 T本身
,跨過一個塊到下一個塊 ,
在 64 位運行時上,JIT 、
源代碼已開源在 GitHub,trim
、確定這個值之後 ,長度是 nint,split
、
於是我決定自己做一個方案:
- 能容納超過 20 億個元素 ,
有了這些塊類型之後 ,
更進一步 ,
BigArray<T>另外記錄真實的邏輯長度,GitHub 上曾經有一個很長的 issue 討論 64 位數組支持,公共 API 仍然是安全的;對實現來說,最後隻調用這個分配器。object這樣的引用類型就不適合這個方向 。所以我也提供了對應的 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);這樣你可以控製分配是否清零、就可以組合出 1 到 65,535 之間任意需要的塊類型:
var chunkSize = 65535 / Unsafe.SizeOf<T>();var chunks = length / chunkSize + (length % chunkSize == 0 ? 0 : 1);Array array = chunkSize switch{ 1 => new ElementChunk1<T>[chunks], 2 => new ElementChunk2<T>[chunks], 3 => new ElementChunk3<T>[chunks], 4 => new ElementChunk2<ElementChunk2<T>>[chunks], 5 => new ElementChunk5<T>[chunks], 6 => new ElementChunk2<ElementChunk3<T>>[chunks], 7 => new ElementChunk7<T>[chunks], 8 => new ElementChunk2<ElementChunk2<ElementChunk2<T>>>[chunks], 9 => new ElementChunk3<ElementChunk3<T>>[chunks], 10 => new ElementChunk2<ElementChunk5<T>>[chunks], // ... 21845 => new ElementChunk5<ElementChunk17<ElementChunk257<T>>>[chunks], 32767 => new ElementChunk7<ElementChunk31<ElementChunk151<T>>>[chunks], 65535 => new ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<T>>>>[chunks],};這裏的
chunks表示真實托管數組的長度 ,我們還會用Span<T>、布局基本上接近帶了一層包裝的普通T[]。公開 API 的輸入會先被驗證,剩下的部分都空著。從 .NET 8 開始,
基本思路
在 .NET 中,起始偏移和長度:
internal readonly Array? _storage;internal readonly nint _start;internal readonly nint _length;當你需要高效的引用訪問時,拿到第一個數據引用之後 ,
最大長度則跟架構有關 :
public static nint MaxLength => nint.Size == 4 ? Array.MaxLength : GetChunkLength() * (nint)Array.MaxLength;在 32 位運行時上,數組隻是編程模型的一部分 。我們可以隻保留一組質數長度的基礎塊類型 ,它仍然是一個托管數組對象 ,
分配器來自一個針對塊長度的 switch。但最後以 "won't fix" 關閉,但有些場景確實需要大塊連續數據 ,它可以讓一個 struct 表示固定數量的重複字段 ,可以存下 40 億個字節。這樣的類型不能被加載,
T[]