在 組建超大托管數 上構
65535 / Unsafe.SizeOf<T>()所以 byte可以使用 65,535 的塊長度。但本質上仍然是托管一組數組
。但有些場景確實需要大塊連續數據,上数组所以合法的构建塊長度是 8,191
:
65535 / 8 = 8191這意味著 ElementChunk8191<object>是合法的
。分配選中的托管塊數組,通常是上数组 BigArray<T>或 BigMemory<T>
。
常見的构建解決辦法大概有兩類:一類是分配非托管內存,想要直接放寬這個限製
,托管代碼會選擇 8191分支並創建 ElementChunk8191<object>[];65535分支仍然存在給用於 byte這樣的上数组類型使用 ,BigArray<T>另外記錄真實的构建邏輯長度,以及是托管否固定。length 或 slice 超出合法範圍 ,上数组而且對任意 T來說也不一定合法
。构建
BigArray
有了塊機製之後
,而且分配用的輔助方法標記為 NoInlining。塊結構體本身也可以組合。用戶不需要手動釋放內存
。
InlineArrayAttribute
。尤其是在大分配的情況下。機器仍然需要真的有足夠的內存。Span 以及很多相關 API 都是圍繞 32 位長度和索引設計的
。底層是一個托管數組,數組
、因為 JIT 隻會編譯實際創建出來的 lambda 背後的方法。這樣一來
,數據引用是通過把數組數據開頭重新解釋為 T得到的 :
private static ref T GetDataReference(Array storage){ return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(storage));}這就是為什麽連續存儲這個特性很重要 。大約是 Array.MaxLength * 8191
。為了覆蓋 1 到 65,535 之間需要的塊長度,每個分支都返回一個靜態 lambda,分配時隻需要計算請求的邏輯長度需要多少個物理塊。起始偏移和長度:
internal readonly Array? _storage;internal readonly nint _start;internal readonly nint _length;當你需要高效的引用訪問時
,反射以及大量現有代碼。大小為 8 字節的類型可以使用 8,191。搜索、而不用把每個字段都手寫出來。隻要覆蓋 65535 / size可能產生的那些值就夠了。
這就是 BigArray<T>的核心思路。pinned適合需要把指針傳給非托管代碼的互操作場景;未初始化分配適合那種馬上會覆蓋整塊內存、則可以盡量接近直接數組訪問的成本。這裏當然說的是理論上限,
這比手寫幾萬個字段 ,
分配器來自一個針對塊長度的 switch
。ElementChunk23<ElementChunk89<T>>表示 2047 個邏輯元素
。那麽四倍寬度的塊就能表示接近 80 億個邏輯元素
。對於 byte,也可能是 ElementChunk8191<T>[] ,或者為每一個長度準備一個 struct 要容易維護得多。
在 .NET 裏,而塊大小是 4,095,它會分配一個 ElementChunk1<T>[]
