关键在于用结构体+连续数组替代List,实现内存物理连续、免GC、零拷贝;优先用Point3D[]等struct数组,配合Span切片、预分配索引管理及内存池优化。
直接把大批量捕获对象从
搬到连续一维数组(如
或
),不是简单“换容器”,而是绕过托管堆频繁分配、GC扫描和缓存不友好的关键一步。真正起效的核心,在于让数据在内存中物理连续、访问可预测、操作免拷贝。
优先用结构体 + 数组替代类对象列表
如果捕获对象本身是轻量级、不可变或生命周期明确的数据(比如坐标点、传感器采样值、帧像素数据),应定义为
而非
:
结构体默认栈分配或内联在数组中,避免每个对象单独堆分配
数组天然连续,CPU 缓存预取高效,遍历速度通常比
快 2–3 倍
示例:
—— 所有 10 万点紧挨着排布,无指针跳转
用 Span 零拷贝切片与复用
若需对原始数据做子集处理(如只分析某帧中间 ROI 区域),别再
或
:
—— 不分配新内存,仅生成轻量视图
可在循环中反复复用同一块数组 + 不同
视图,彻底消除 GC 压力
预分配固定大小数组 + 索引管理代替动态扩容
的
在容量不足时触发数组翻倍复制,O(n) 开销累积明显。重构策略是:
根据业务上限(如每秒最多捕获 5000 个对象),一次性分配足额数组:
用整型计数器
追踪当前有效长度,写入时直接索引赋值:
后续处理统一基于
,安全且零开销
配合内存池应对高频创建/销毁场景
若捕获对象虽为
但大小固定、复用率高(如网络包解析后的
),可结合对象池:
池预先分配大块连续内存(如
)
用
划分区域,配合
定位对象首地址
避免
触发堆分配,实测单位创建耗时可从 1500ns 降至 400ns
ListT[]SpanstructclassT[]ListPoint3D[] points = new Point3D[100000];ToArray()GetRange()Span span = points.AsSpan(); Span roi = span.Slice(startIndex, length); SpanListAdd()var buffer = new CaptureItem[5000];_countbuffer[_count++] = item;buffer.AsSpan(0, _count)classPacketHeaderbyte[] poolBuffer = new byte[1024 * 1024];SpanUnsafe.AsRefnew T()