创建仓库
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
---
|
||||
tags: []
|
||||
date created: 星期一, 十一月 24日 2025, 11:19:42 晚上
|
||||
date modified: 星期一, 十一月 24日 2025, 11:20:43 晚上
|
||||
---
|
||||
|
||||
# 2.5.4 零拷贝数据容器规范 (Zero-Copy Data Container Specification)
|
||||
|
||||
**基线核心宗旨**:**“RAII for Ownership, Header for Context (RAII 管资产,Header 管上下文)”**。
|
||||
容器必须是 **Movable-Only (仅可移动)** 的,严禁拷贝。它利用 C++ 类型系统强制执行所有权的单一性,并通过标准化的 Header 确保全链路的可观测性。
|
||||
|
||||
-----
|
||||
|
||||
## 1\. 核心设计契约 (Design Contracts)
|
||||
|
||||
1. **仅移动语义 (Move-Only Semantics)**:
|
||||
|
||||
- `DataPacket<T>` 的拷贝构造函数和拷贝赋值操作符必须被 **显式删除 (`= delete`)**。
|
||||
- **理由**:防止开发者无意中触发深拷贝,破坏零拷贝原则;防止多个对象持有同一块物理内存的所有权,导致 Double Free。
|
||||
|
||||
2. **统一元数据 (Unified Metadata)**:
|
||||
|
||||
- 所有流转的数据包,无论其负载是原始波形还是航迹,都必须携带相同的 Header 结构。
|
||||
- **理由**:使得中间件(如调度器、监控探针)可以在不解析 Payload 的情况下读取 `TraceID` 和 `Timestamp`。
|
||||
|
||||
3. **自动回收 (Self-Reclamation)**:
|
||||
|
||||
- 容器析构时,必须自动触发 Payload 的资源释放(归还内存池或释放堆内存)。
|
||||
- **理由**:消除内存泄漏风险,简化模块内的错误处理逻辑(异常安全)。
|
||||
|
||||
-----
|
||||
|
||||
## 2\. 通用容器定义 (Generic Definition)
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 通用零拷贝数据容器
|
||||
* @tparam PayloadType 负载类型 (必须支持移动语义)
|
||||
*/
|
||||
template <typename PayloadType>
|
||||
struct DataPacket {
|
||||
// --- 1. 标准化头部 (Fixed Header) ---
|
||||
struct Header {
|
||||
// 全链路追踪 ID (从 TraceContext 继承)
|
||||
uint64_t trace_id;
|
||||
|
||||
// 数据生成时间戳 (UTC Microseconds, 总控授时)
|
||||
uint64_t timestamp_us;
|
||||
|
||||
// 流水线序列号 (用于检测内部丢包/乱序)
|
||||
uint64_t sequence_id;
|
||||
|
||||
// 源模块标识 (用于调试拓扑)
|
||||
uint32_t source_module_id;
|
||||
|
||||
// 标志位 (e.g., END_OF_STREAM, CONFIG_UPDATE_BARRIER)
|
||||
uint32_t flags;
|
||||
} header;
|
||||
|
||||
// --- 2. 业务负载 (Flexible Payload) ---
|
||||
PayloadType payload;
|
||||
|
||||
// --- 3. 构造与移动语义 ---
|
||||
|
||||
// 默认构造
|
||||
DataPacket() = default;
|
||||
|
||||
// 显式移动构造
|
||||
DataPacket(DataPacket&& other) noexcept
|
||||
: header(other.header), payload(std::move(other.payload)) {}
|
||||
|
||||
// 显式移动赋值
|
||||
DataPacket& operator=(DataPacket&& other) noexcept {
|
||||
if (this != &other) {
|
||||
header = other.header;
|
||||
payload = std::move(other.payload);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 严禁拷贝 !!!
|
||||
DataPacket(const DataPacket&) = delete;
|
||||
DataPacket& operator=(const DataPacket&) = delete;
|
||||
};
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 3\. 核心特化与所有权管理 (Specializations & Ownership)
|
||||
|
||||
根据 **2.5.1** 定义的业务对象,我们需要针对两类不同性质的数据定义具体的容器行为。
|
||||
|
||||
### 3.1 原始回波数据包 (`RawDataPacket`)
|
||||
|
||||
- **场景**:`DataReceiver` -\> `SignalProcessor`。数据量极大(MB 级),必须使用页锁定内存池。
|
||||
- **Payload 类型**:`std::unique_ptr<DataObject, MemoryPoolDeleter>`。
|
||||
- **所有权逻辑**:
|
||||
- `RawDataPacket` 持有 `unique_ptr`。
|
||||
- 当 Packet 在队列中移动时,`unique_ptr` 随之移动。
|
||||
- 当 Packet 在消费端(`SignalProcessor`)析构时,`MemoryPoolDeleter` 被调用,将底层的 `void*` 归还给 `PinnedMemoryPool`。
|
||||
|
||||
<!-- end list -->
|
||||
|
||||
```cpp
|
||||
// 定义特定的删除器
|
||||
struct MemoryPoolDeleter {
|
||||
IMemoryPool* pool;
|
||||
void operator()(void* ptr) const {
|
||||
if (pool && ptr) pool->release(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// 别名定义
|
||||
using RawPayload = std::unique_ptr<DataObject, MemoryPoolDeleter>;
|
||||
using RawDataPacket = DataPacket<RawPayload>;
|
||||
```
|
||||
|
||||
### 3.2 结果数据包 (`DetectionPacket` / `TrackPacket`)
|
||||
|
||||
- **场景**:`SignalProcessor` -\> `DataProcessor` -\> `DisplayController`。数据量较小(KB 级),使用堆内存或对齐分配器。
|
||||
- **Payload 类型**:`std::vector<DetectionResult>` (Aligned)。
|
||||
- **所有权逻辑**:
|
||||
- `std::vector` 本身就是 RAII 容器。
|
||||
- `DataPacket` 的移动会自动触发 `vector` 的移动(仅交换内部指针 `begin/end/capacity`),成本极低(3 个指针大小)。
|
||||
- **注意**:此处不需要自定义删除器,除非我们想引入“对象池”来复用 vector 的内存(对于 100Hz 的频率,`std::vector` 的默认分配器性能通常可以接受,暂不引入对象池以降低复杂度)。
|
||||
|
||||
<!-- end list -->
|
||||
|
||||
```cpp
|
||||
// 别名定义
|
||||
// 使用 2.5.1 定义的对齐容器
|
||||
using DetectionPayload = AlignedVector<DetectionResult>;
|
||||
using DetectionPacket = DataPacket<DetectionPayload>;
|
||||
|
||||
using TrackPayload = AlignedVector<TrackData>;
|
||||
using TrackPacket = DataPacket<TrackPayload>;
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 4\. 辅助工厂方法 (Factory Helpers)
|
||||
|
||||
为了简化 Header 的填充(防止开发者忘记填 TraceID),应提供工厂函数。
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
DataPacket<T> MakePacket(T&& payload, uint64_t seq_id) {
|
||||
DataPacket<T> packet;
|
||||
|
||||
// 自动捕获当前上下文
|
||||
packet.header.trace_id = TraceContext::getCurrentId();
|
||||
packet.header.timestamp_us = Clock::nowInUs();
|
||||
packet.header.sequence_id = seq_id;
|
||||
packet.header.source_module_id = ModuleContext::getCurrentModuleId();
|
||||
packet.header.flags = 0;
|
||||
|
||||
// 移动负载
|
||||
packet.payload = std::move(payload);
|
||||
|
||||
return packet;
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## 总结
|
||||
|
||||
**2.5.4 章节基线** 已确立为:
|
||||
|
||||
1. **Generic Envelope**:统一的 `DataPacket<T>` 模板,强制包含 Header。
|
||||
2. **Move-Only**:通过删除拷贝构造函数,物理上杜绝深拷贝。
|
||||
3. **Hybrid Payload Strategy**:
|
||||
- 大内存(Raw):`unique_ptr` + `CustomDeleter` (Pool)。
|
||||
- 小内存(Result):`std::vector` (Move Semantics)。
|
||||
|
||||
这一设计确保了数据在“集装箱”里流转时,既安全(不会泄露),又轻快(只有指针在动)。
|
||||
Reference in New Issue
Block a user