创建仓库

This commit is contained in:
2025-12-11 07:24:36 +08:00
commit 0d81c1792d
128 changed files with 15104 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
---
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 计算效率,确保数据进入算法时是“最可口”的形态。