托管數在 組建超大 上構
這比手寫幾萬個字段 ,托管反射以及大量現有代碼 。上数组索引也使用 nint 。构建但最後以 "won't fix" 關閉
,托管像 string、上数组
有了這些塊類型之後,构建數組隻是托管編程模型的一部分。object這樣的上数组引用類型就不適合這個方向。再把這些塊裏的构建數據看成一段連續的 T
。其他長度都可以由這些基礎長度相乘得到
。托管實現內部如果需要調用隻接受 Span<T>或 ReadOnlySpan<T>的上数组 BCL API,
[InlineArray(4)]struct FourStrings{ private string _first;}它也能用於泛型:
[InlineArray(4)]struct FourElements<T>{ private T _first;}這樣一來 ,构建這裏我們不需要在每次訪問時都除以塊大小。托管但本質上仍然是一組數組 。
BigMemory<byte> page = buffer.AsBigMemory(1024, 4096);page.Span.Fill(0);API 的設計則盡量沿用了普通 Span/Memory 的習慣 :切片 、
這種做法會不會多分配一些沒有用到的空間?答案是會 ,最大長度會隨塊大小增長 。代碼不會執行和類型不會被加載不能簡單畫等號 。從零開始的數組是 SZArray,
[MethodImpl(MethodImplOptions.NoInlining)]private static Array AllocateArray<TElement>(int chunks, bool pinned, bool uninitialized){ return uninitialized ? GC.AllocateUninitializedArray<TElement>(chunks, pinned) : GC.AllocateArray<TElement>(chunks, pinned);}這裏強行要求間接調用很關鍵。則可以盡量接近直接數組訪問的成本。起始偏移和長度 :
internal readonly Array? _storage;internal readonly nint _start;internal readonly nint _length;當你需要高效的引用訪問時,普通 .NET 代碼裏,隻是每個元素更大
。最常見的一維
、你需要管理每個內部數組的大小,可以存下 40 億個字節。公共 API 仍然是安全的;對實現來說,對某個 T來說 ,ElementChunk23<ElementChunk89<T>>表示 2047 個邏輯元素。是為每一種塊長度都定義一個類型:
[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; }這顯然不現實 ,
通常不太建議隨意使用巨大的數組。隻是查看由別的對象保持存活的內存,而且對任意 T來說也不一定合法 。尤其是在大分配的情況下。
從 .NET 8 開始,
構建塊類型
最直觀的實現,這樣塊類型數量從 65,535 降到了 510,
所以第一個想法很簡單:讓一個數組元素代表多個邏輯元素。但仍然不少
。它會讓 GC 壓力更大,ToArray、也就是 65,535,而不是元素背後的字節數
。GC 、如果隻是想使用的話可以從 NuGet 引用包來使用 。可以寫成:
ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<byte>>>>因為:
3 * 5 * 17 * 257 = 65535因此,就把數據拆成能放進 int的片段來處理 。大約是 Array.MaxLength * 8191。隻有和當前 Unsafe.SizeOf<T>()匹配的塊形狀會真正實例化,它們的 Span屬性會生成 BigSpan<T>或 BigReadOnlySpan<T>