当前位置: 首页 > news >正文

BigArray#

有了块机制之后,BigArray<T>本身可以保持得很小。

它只保存两个东西:

Copy

internal readonly Array _storage; internal readonly nint _length;

普通长度下,它会分配一个ElementChunk1<T>[],布局基本上接近带了一层包装的普通T[]。更大的长度下,它会计算块长度,分配选中的块数组,并把逻辑长度记录为nint

这也是为什么_storage的类型是Array:实际运行时类型取决于T。它可能是ElementChunk1<T>[],也可能是ElementChunk8191<T>[],或者是ElementChunk3<ElementChunk5<ElementChunk17<ElementChunk257<T>>>>[]这样的组合块类型。

Copy

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; }

然后是索引器实现。这里我们不需要在每次访问时都除以块大小。底层是一个托管数组,数组数据区里连续排列着块结构体,而元素又内联保存在这些块里,因此代码只需要拿到第一个逻辑T的引用,然后用普通的引用偏移往后移动。

Copy

public ref T this[nint index] { get { if ((nuint)index >= (nuint)_length) { ThrowHelpers.ThrowOutOfRange(nameof(index)); } return ref Unsafe.Add(ref GetDataReference(), index); } }

这里确实用到了Unsafe,但它只藏在实现内部。公开 API 的输入会先被验证,然后实现使用引用偏移,避免每一次逻辑访问都再走一次普通数组边界检查。如果 index、length 或 slice 超出合法范围,会在到达这条路径之前失败。对用户来说,公共 API 仍然是安全的;对实现来说,则可以尽量接近直接数组访问的成本。

数据引用是通过把数组数据开头重新解释为T得到的:

Copy

private static ref T GetDataReference(Array storage) { return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(storage)); }

这就是为什么连续存储这个特性很重要。拿到第一个数据引用之后,Unsafe.Add(ref first, index)会移动index个逻辑T元素。跨过一个块到下一个块,只是在同一段数组数据区里继续往前走。这样一来,BigArray<T>不需要像交错数组包装器那样在每次访问时都做除法和取余;它只是把一个托管数组对象视作一段更大的逻辑序列。

最大长度则跟架构有关:

Copy

public static nint MaxLength => nint.Size == 4 ? Array.MaxLength : GetChunkLength() * (nint)Array.MaxLength;

在 32 位运行时上,nint本身无法表示更大的索引空间,所以BigArray<T>保持普通数组的限制。

在 64 位运行时上,最大长度会随块大小增长。对byte来说,大约是Array.MaxLength * 65535;对 64 位运行时上的long或对象引用来说,大约是Array.MaxLength * 8191。对于byte,这意味着它理论上可以表示接近 128 TiB 的数组,准确地说是 127.998 TiB。这里当然说的是理论上限,机器仍然需要真的有足够的内存。

BigSpan 和 BigMemory#

只有持有存储的类型还不够。普通 .NET 代码里,数组只是编程模型的一部分。我们还会用Span<T>ReadOnlySpan<T>Memory<T>ReadOnlyMemory<T>来传递视图。

BigSpan<T>是一个面向超大连续区域的栈上视图:

Copy

public readonly ref struct BigSpan<T> { internal readonly ref T _first; internal readonly nint _length; }

它的基本形状和Span<T>一样:一个起始引用加一个长度。不同的是,长度是nint,索引也使用nint。和Span<T>一样,它不拥有内存,只是查看由别的对象保持存活的内存,通常是BigArray<T>BigMemory<T>

Copy

BigArray<byte> buffer = new((nint)Array.MaxLength + 1024); BigSpan<byte> span = buffer.AsBigSpan(); span[Array.MaxLength] = 42;

BigMemory<T>BigReadOnlyMemory<T>则是可以保存起来的视图。它们记录底层托管数组、起始偏移和长度:

Copy

internal readonly Array? _storage; internal readonly nint _start; internal readonly nint _length;

当你需要高效的引用访问时,它们的Span属性会生成BigSpan<T>BigReadOnlySpan<T>。由于BigMemory<T>把底层托管数组保存在_storage里,它可以被放进字段或从方法返回,同时仍然让这段存储对 GC 可见。

Copy

BigMemory<byte> page = buffer.AsBigMemory(1024, 4096); page.Span.Fill(0);

API 的设计则尽量沿用了普通 Span/Memory 的习惯:切片、复制、搜索、排序、trim、split、ToArrayToBigArray以及只读转换。实现内部如果需要调用只接受Span<T>ReadOnlySpan<T>的 BCL API,就把数据拆成能放进int的片段来处理。

BigSpan<T>并不指望让所有现有 API 都接受超过int.MaxValue个元素。它给你一个大索引视图,并且在需要和现有 API 互操作时,允许你取出普通的Span<T>片段。

Copy

nint offset = (nint)5_000_000_000L; Span<byte> window = buffer.AsSpan(offset, length: 4096);

分配 API#

最简单的分配方式自然是调用构造函数:

Copy

nint length = (nint)10_000_000_000L; BigArray<byte> buffer = new(length);

不过 .NET 的数组也有显式的 GC 分配辅助方法,所以我也提供了对应的 API:

Copy

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);

http://www.jsqmd.com/news/1139647/

相关文章:

  • .NET 10 的正则性能现在什么水平?我拿它和 Go、Python、C++、PCRE2 测了一轮
  • WaveTools:一键解锁《鸣潮》120FPS高帧率与画质优化完整指南
  • 北京华恒智信为酒店行业搭建三维人才分流体系化解业务用人难题
  • 跨平台视频解密工具:轻松下载微信视频号、抖音等加密资源
  • 3分钟制作USB启动盘:Rufus极速格式化工具全攻略
  • ORB-SLAM3 SearchByBoW 2
  • 写在开头:我为什么开这个专栏?
  • 今天偶然发现有一个评论有500个赞
  • 宝可梦实景图数据集在无人机导航中的计算机视觉应用
  • 终极指南:大麦网双端自动抢票技术方案与实战部署
  • TwinCAT3 3.1 配置汇川 SV660N:EtherCAT 扫描与 8388608 分辨率设置详解
  • 摩托车混流装配防错引导系统硬件怎么选?海雅达 Model 10X 工业三防平板电脑采购预算与部署成本评估
  • 成对评估上包络线:有界指标的稳健效果边界计算方法
  • Metasploit进阶:从set payload到generate命令的Shellcode生成与免杀实战
  • Linux防火墙极简配置指南:10分钟掌握firewalld核心安全策略
  • 常见硬件设计问题分析及解决方案-EMC静电问题
  • Seedance 2.5本地AI生图与视频生成:部署指南与参数调优
  • F28377D Modbus RTU移植:硬件时序、寄存器映射与实时性硬核实践
  • 从SSTI模板注入到docker容器内网渗透:靶机练习之Tempus_fugit3
  • Windows系统下Xray被动扫描环境配置全攻略:从证书安装到代理设置
  • 狼人杀不只是游戏:9 个大模型的多轮博弈能力观察
  • 2026年东莞良心4s店哪家服务好:最新权威排名与专业指南。
  • 洛雪音乐音源完全指南:如何免费获取全网无损音乐资源
  • 如何快速掌握res-downloader:全网视频音频资源下载的终极指南
  • Snipe-IT新手部署全路径指南:XAMPP/Ubuntu/Docker三路实操
  • 基于行车记录仪与AI的道路病害自动检测技术实践
  • 从“人工智障”到“智能管家”:手把手教你重构一个R语言AI智能体
  • WindowResizer:Windows窗口调整终极解决方案,告别无法调整大小的顽固窗口
  • PCB孔金属化工艺:从化学镀铜到直接电镀的2种方案与15μm厚度控制
  • Fortify路径遍历漏洞深度解析:从输入验证到数据流防护的完整方案