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

171 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
tags: []
date created: 星期一, 十一月 24日 2025, 5:32:36 下午
date modified: 星期一, 十一月 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)**
- 所有业务对象必须是 **Trivial****Standard-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++ 结构体定义 (基线)**
```cpp
/**
* @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++ 结构体定义 (基线)**
```cpp
/**
* @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 预取器可以直接将 `state``covariance` 拉入 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 中,我们不存储单个对象,而是存储对象的数组。
```cpp
// 定义专属分配器,确保 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 步骤)是非常缓存友好的。
```plaintext
| <--- 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 Alignment**`DetectionResult` 16B 对齐,`TrackData` 32B 对齐。
3. **Explicit Padding**:显式填充所有空隙,消除未定义行为。
4. **Aligned Container**:使用带对齐分配器的 Vector 承载数据。
这套模型直接服务于 **DataProcessor** 的 CPU 计算效率,确保数据进入算法时是“最可口”的形态。