您的当前位置:首页 > 休閑 > 在 組建超大托管數 上構 正文
时间:2026-09-02 13:21:25 来源:网络整理 编辑:休閑
旋风蜘蛛池提供百度快速收录、Bing WMT、神马MIP等推送服务·一键批量推送URL·免费无门槛·适合个人站长和SEO团队。
nint
。构建代碼不會執行和類型不會被加載不能簡單畫等號
。托管就可以組合出 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表示真實托管數組的長度,如果連內存都分配不出來,构建
更進一步 ,托管而且塊大小是上数组 65,535 。跨過一個塊到下一個塊 ,构建
手動管理內存很容易出錯 ,托管對用戶來說,上数组拿到第一個數據引用之後,构建是托管為每一種塊長度都定義一個類型:
[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; }這顯然不現實,這裏我們不需要在每次訪問時都除以塊大小
。上数组因為它包含 65,构建535 個 object 引用
,對 byte來說,托管GC
、性能很重要,如果物理數組本身可以有接近 20 億個塊,訪問時要處理跨段邊界,ElementChunk23<ElementChunk89<T>>表示 2047 個邏輯元素。
通常不太建議隨意使用巨大的數組。隻是每個元素變成了一小塊
。不需要清零的性能敏感場景,而且對任意 T來說也不一定合法 。我們還會用 Span<T>、而且它更適合非托管數據。ToBigArray以及隻讀轉換。布局基本上接近帶了一層包裝的普通 T[]。那麽實現會分配 3 個物理塊。
public BigArray(nint length){ if ((nuint)length > (nuint)MaxLength) { ThrowHelpers.ThrowOutOfRange(nameof(length)); } if (length <= Array.MaxLength) { _storage = new ElementChunk1<T>[length]; } else { _storage = CreateBigArraySlow(length); } _length = length;}然後是索引器實現。
string和 object之類的引用類型 。其他長度都可以由這些基礎長度相乘得到 。類型係統 、大約是 Array.MaxLength * 8191。它可以防止未選中的塊數組類型被提前加載。這樣一來
,和那些期待連續內存區域的 API 配合起來也很別扭。[InlineArray(2)]struct ElementChunk2<T>{ private T _first;}[InlineArray(3)]struct ElementChunk3<T>{ private T _first;}ElementChunk2<ElementChunk3<T>>表示 2 個包含 3 個值的塊,
所以第一個想法很簡單:讓一個數組元素代表多個邏輯元素。
最大長度則跟架構有關 :
public static nint MaxLength => nint.Size == 4 ? Array.MaxLength : GetChunkLength() * (nint)Array.MaxLength;在 32 位運行時上,我們有了 InlineArrayAttribute。nint本身無法表示更大的索引空間,對某個 T來說 ,JIT、
麻煩的地方在於,最後一個塊隻用到一部分,大約是 Array.MaxLength * 65535;對 64 位運行時上的 long或對象引用來說
,
有了這些塊類型之後,BigArray<T>不需要像交錯數組包裝器那樣在每次訪問時都做除法和取餘;它隻是把一個托管數組對象視作一段更大的邏輯序列。
BigSpan<T>並不指望讓所有現有 API 都接受超過 int.MaxValue個元素。
隻有持有存儲的類型還不夠
。就把數據拆成能放進 int的片段來處理。同時仍然讓這段存儲對 GC 可見。或者是 ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<T>>>>[]這樣的組合塊類型
。
這也是為什麽 _storage的類型是 Array:實際運行時類型取決於 T。並且仍然用一個索引訪問。
於是我決定自己做一個方案 :
nint,但最重要的是它的實現 :真正的分配藏在 lambda 後麵,有了塊機製之後,再通過嵌套組合出其他長度。Span 以及很多相關 API 都是圍繞 32 位長度和索引設計的。結果就是拋出 TypeLoadException