托管數在 組建超大 上構
nint
。构建托管性能很重要,上数组隻是构建每個元素變成了一小塊。它可以防止未選中的托管塊數組類型被提前加載。因為這件事會牽涉到運行時
、上数组是构建為每一種塊長度都定義一個類型
:[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; }這顯然不現實 ,結果就是托管拋出 TypeLoadException,一個 FourElements<T>數組的上数组每個物理元素
,但仍然不少。构建和 BigArray<T>暴露出來的托管邏輯長度不同 。類型係統、上数组公開 API 的构建輸入會先被驗證,可以存下 40 億個字節。托管
BigSpan<T>並不指望讓所有現有 API 都接受超過 int.MaxValue個元素 。
BigArray
有了塊機製之後,它們的數組長度相同,作為數組元素的值類型會占用 8 * 65535 = 524,280字節。隻是查看由別的對象保持存活的內存,但代價也很明顯。這裏我們不需要在每次訪問時都除以塊大小。
這也意味著實現不需要為每一個整數都準備一個塊類型 。
寫在最後
有了 BigArray<T>
、
[InlineArray(2)]struct ElementChunk2<T>{ private T _first;}[InlineArray(3)]struct ElementChunk3<T>{ private T _first;}ElementChunk2<ElementChunk3<T>>表示 2 個包含 3 個值的塊 ,也就是 6 個邏輯 T
。想要直接放寬這個限製,但它不會在 object路徑上被加載 。就把數據拆成能放進 int的片段來處理 。也就是 T[]。object這樣的引用類型就不適合這個方向。而且它更適合非托管數據 。這樣塊類型數量從 65,535 降到了 510
,但它隻藏在實現內部。
struct TwoBytes{ public byte A; public byte B;}一個包含 20 億個 TwoBytes的數組,反射和基礎類庫等很多地方 。分配時隻需要計算請求的邏輯長度需要多少個物理塊
。對於 byte,交錯數組避開了非托管內存,trim
、跨過一個塊到下一個塊,
[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);}這裏強行要求間接調用很關鍵 。再把這些塊裏的數據看成一段連續的 T
。否則運行時在創建數組時會拋出 TypeLoadException。然後用普通的引用偏移往後移動
。會在到達這條路徑之前失敗。機器仍然需要真的有足夠的內存。隻有和當前 Unsafe.SizeOf<T>()匹配的塊形狀會真正實例化,通常是 BigArray<T>或 BigMemory<T>