Files
Inbox/系统基座文件/2/2.5/2.5.4 零拷贝数据容器规范 (Zero-Copy Data Container Specification).md
2025-12-11 07:24:36 +08:00

6.1 KiB
Raw Blame History

tags, date created, date modified
tags date created date modified
星期一, 十一月 24日 2025, 11:19:42 晚上 星期一, 十一月 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 的情况下读取 TraceIDTimestamp
  3. 自动回收 (Self-Reclamation)

    • 容器析构时,必须自动触发 Payload 的资源释放(归还内存池或释放堆内存)。
    • 理由:消除内存泄漏风险,简化模块内的错误处理逻辑(异常安全)。

2. 通用容器定义 (Generic Definition)

/**
 * @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
// 定义特定的删除器
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 的默认分配器性能通常可以接受,暂不引入对象池以降低复杂度)。
// 别名定义
// 使用 2.5.1 定义的对齐容器
using DetectionPayload = AlignedVector<DetectionResult>;
using DetectionPacket = DataPacket<DetectionPayload>;

using TrackPayload = AlignedVector<TrackData>;
using TrackPacket = DataPacket<TrackPayload>;

4. 辅助工厂方法 (Factory Helpers)

为了简化 Header 的填充(防止开发者忘记填 TraceID应提供工厂函数。

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
    • 大内存Rawunique_ptr + CustomDeleter (Pool)。
    • 小内存Resultstd::vector (Move Semantics)。

这一设计确保了数据在“集装箱”里流转时,既安全(不会泄露),又轻快(只有指针在动)。