Files
Inbox/系统基座文件/2/2.5/2.5.1 内部高性能业务对象模型 (Internal High-Performance Business Object Model).md
2025-12-11 07:24:36 +08:00

7.8 KiB
Raw Permalink Blame History

tags, date created, date modified
tags date created date modified
星期一, 十一月 24日 2025, 5:32:36 下午 星期一, 十一月 24日 2025, 10:39:00 晚上

2.5.1 内部高性能业务对象模型 (Internal High-Performance Business Object Model)

基线核心宗旨“Hardware-First (硬件优先)”。

内部数据对象的设计不再是为了“方便编程”,而是为了适配 CPU 缓存行 (Cache Line)、迎合 SIMD 指令集 (AVX/NEON) 以及 最小化内存带宽消耗。


1. 核心设计契约 (Design Contracts)

在定义具体结构体之前,必须确立以下不可逾越的“红线”:

  1. 严格 POD (Plain Old Data)

    • 所有业务对象必须是 TrivialStandard-Layout 的。
    • 禁止:虚函数 (virtual)、智能指针成员 (std::shared_ptr)、堆内存容器成员 (std::vector, std::string)。
    • 理由:确保对象可以被 memcpy 直接拷贝(虽然我们用零拷贝,但在某些边界仍需落盘或调试),且在内存中是连续的,对 CPU 预取器Prefetcher友好。
  2. 强制对齐 (Forced Alignment)

    • 利用 C++11 alignas 关键字,强制结构体大小为 16 字节 (128-bit)32 字节 (256-bit) 的倍数。
    • 理由:适配 ARM NEON (128-bit) 和 x86 AVX2 (256-bit) 寄存器宽度,允许编译器生成向量化加载/存储指令 (vld1q, vmovaps),而非逐个标量读写。

2. 业务对象定义基线

2.1 点迹对象:DetectionResult

这是信号处理流水线产出的最高频数据(每秒可能数万个),是 CPU数据处理模块的数据密集型计算对象。

C++ 结构体定义 (基线)

/**
 * @brief 单个检测点迹 (Plot/Detection)
 * @note 强制 16 字节对齐,适配 SSE/NEON 128位寄存器
 * @size 48 Bytes (3个 128-bit 块)
 */
struct alignas(16) DetectionResult {
    // Block 0: 空间坐标 (16 Bytes) -> 可以直接加载到一个向量寄存器
    float range;        // 距离 (m)
    float azimuth;      // 方位角 (rad)
    float elevation;    // 俯仰角 (rad)
    float velocity;     // 径向速度 (m/s)

    // Block 1: 信号质量与辅助信息 (16 Bytes)
    float snr;          // 信噪比 (dB)
    float power;        // 信号功率
    float noise;        // 噪声底
    uint32_t channel_id;// 通道/波束ID (用于区分多源)

    // Block 2: 索引与标识 (16 Bytes)
    uint32_t range_idx; // 距离门索引
    uint32_t doppler_idx; // 多普勒索引
    uint32_t batch_id;  // 所属的 CPI 批次 ID
    uint32_t padding;   // [显式填充] 保持 16B 对齐
};

设计决策辩护

  • Float vs Double:选用 float (32-bit)。对于雷达点迹处理,单精度浮点数精度通常已足够,且相比 double 能让 SIMD 吞吐量翻倍(一个 AVX2 寄存器处理 8 个 float vs 4 个 double
  • Block 0 布局:将 r, azi, ele, v 紧凑排列,使得坐标转换算法(极坐标转笛卡尔坐标)可以一次加载 Block 0 并执行向量运算。
  • Explicit Padding:末尾增加 padding 字段,不仅为了凑齐 48 字节,更是为了防止隐式填充导致的内存脏数据风险。

2.2 航迹对象:TrackData

这是数据处理模块的核心状态对象,用于卡尔曼滤波等矩阵运算。此处的改动最大:我们废弃了旧文档中 std::vector 的设计,改为定长数组,以符合 POD 约束。

C++ 结构体定义 (基线)

/**
 * @brief 单个航迹状态 (Track)
 * @note 强制 32 字节对齐,适配 AVX2 256位寄存器
 * @note 假设使用 6维状态向量 (x, y, z, vx, vy, vz)
 */
struct alignas(32) TrackData {
    // --- Header (32 Bytes) ---
    uint64_t track_id;      // 全局唯一航迹 ID
    uint64_t timestamp_us;  // 更新时间戳
    uint32_t status;        // 状态枚举 (Tentative/Confirmed)
    uint32_t hit_count;     // 关联次数
    uint32_t miss_count;    // 丢失次数
    uint32_t station_id;    // 源站 ID (分布式新增)

    // --- State Vector (32 Bytes) ---
    // 6维状态向量最后补 2 个 float padding 凑齐 8 个 float (256-bit)
    // 便于 AVX2 一次加载整个状态向量
    float state[8];         // [x, y, z, vx, vy, vz, pad, pad]

    // --- Covariance Matrix (Diagonal/Simplified) ---
    // 这是一个设计折中。完整的 6x6 协方差矩阵需要 36*4 = 144 Bytes。
    // 为了保持对象轻量,这里仅存储对角线元素 (方差)
    // 或者如果内存允许,展开存储下三角矩阵。
    // 此处演示存储完整对角线 + 填充 (32 Bytes)
    float covariance_diag[8]; // [var_x, var_y, var_z, …, pad, pad]

    // --- Quality & Classification (32 Bytes) ---
    float prob_target;      // 目标存在概率
    float maneuver_indicator;// 机动指示器
    uint32_t type_label;    // 分类标签
    uint32_t _reserved[5];  // 预留空间,保持缓存行对齐
};

设计决策辩护

  • 定长数组 (float state[8]):替代 std::vector
    • 收益内存绝对连续。卡尔曼滤波预测步骤中CPU 预取器可以直接将 statecovariance 拉入 L1 缓存消除指针跳转Pointer Chasing开销。
    • 对齐技巧6 维向量补齐到 8 维,完美契合 AVX2 (256-bit = 8 floats)。这意味着向量加减法只需一条指令。
  • 分块对齐每个数据块Header, State, Covariance都对齐到 32 字节。这确保了字段不会跨越缓存行Cache Line Split消除了跨行访问的惩罚。

3. 容器与内存管理策略

定义了“原子”结构体后,还需要定义装载它们的“容器”。

3.1 容器选型:std::vector + 对齐分配器

DataPacket<T> 的 Payload 中,我们不存储单个对象,而是存储对象的数组。

// 定义专属分配器,确保 vector 的底层内存首地址也是 32 字节对齐的
template <typename T>
using AlignedVector = std::vector<T, boost::alignment::aligned_allocator<T, 32>>;

// 最终在 DataPacket 中的 Payload 类型
using DetectionList = AlignedVector<DetectionResult>;
using TrackList     = AlignedVector<TrackData>;
  • 为什么需要 Aligned Allocator

    普通的 std::vector 只保证元素大小对齐,不保证数组首地址对齐(尽管现代 glibc 通常对齐到 16B。为了让第一条 AVX 指令vmovaps安全执行首地址必须严格对齐。

3.2 内存布局图示 (AoS 布局)

在内存中,一批点迹的物理视图如下。这种 Array of Structures (AoS) 布局对于“逐个点迹处理”的逻辑(如卡尔曼滤波的 Update 步骤)是非常缓存友好的。

| <--- Cache Line 64B ---> | <--- Cache Line 64B ---> |
[ Det 0 (48B) ] [ Det 1 (48B) ] [ Det 2 (48B) ] [ … ]
	^                ^                 ^
 Align(16)        Align(16)         Align(16)
  • 注意:由于 48B 不是 64B 的约数,Det 1 会跨越缓存行边界。
  • 优化选项 (SoA):如果在某些极端性能场景下(如纯粹的坐标转换),我们可以考虑 Structure of Arrays (SoA),即 vector<float> range_list, azimuth_list…。但考虑到代码复杂度和卡尔曼滤波的逻辑特性(通常需要同时访问一个点的所有属性),AoS (上述结构体) 是当前工程最佳平衡点。

总结

2.5.1 章节基线 已确立为:

  1. Strict POD:摒弃 std::vector 成员,全面拥抱定长数组。
  2. SIMD AlignmentDetectionResult 16B 对齐,TrackData 32B 对齐。
  3. Explicit Padding:显式填充所有空隙,消除未定义行为。
  4. Aligned Container:使用带对齐分配器的 Vector 承载数据。

这套模型直接服务于 DataProcessor 的 CPU 计算效率,确保数据进入算法时是“最可口”的形态。