在分布式数据服务(DDS)中,零拷贝(Zero-Copy) 是一项核心的高性能优化技术,旨在消除或最小化数据在发布者与订阅者之间传输过程中的内存复制操作。这对于处理高吞吐量、低延迟、大尺寸数据(如图像、点云、大型结构体)的场景至关重要。
一、 零拷贝技术原理
传统的数据传输流程(存在拷贝):
- 发布端
- 应用程序构造数据对象(位于应用内存)。
- 调用
write()发送数据。 - DDS 中间件将数据对象序列化(转换为一维字节流)到一个内部的发送缓冲区(发生一次内存拷贝)。
- 底层传输协议(如 TCP/UDP)可能需要将数据从 DDS 的发送缓冲区复制到内核的 Socket 缓冲区(可能发生第二次内存拷贝)。
- 网卡驱动将数据从内核缓冲区复制到网卡缓冲区(DMA,通常不被视为 CPU 拷贝开销,但仍是硬件拷贝)。
- 订阅端
- 数据包到达订阅端网卡,通过 DMA 复制到内核的 Socket 缓冲区。
- 用户态 DDS 库将数据从内核缓冲区复制到内部的接收缓冲区(发生一次内存拷贝)。
- DDS 中间件将接收缓冲区中的字节流反序列化重建为数据对象,放入一个由中间件管理的样本池(发生第二次内存拷贝)。
- 应用程序调用
take()/read()获取数据,DDS 将样本池中的数据对象复制或提供引用给应用程序(可能发生第三次内存拷贝,或避免拷贝但需谨慎管理生命周期)。
零拷贝的目标是尽可能消除上述流程中涉及 CPU 参与的内存复制(红色标注部分),特别是应用层与 DDS 中间件之间的拷贝以及 DDS 内部缓冲区之间的拷贝。
核心原理
- 直接内存访问
- 让应用程序和 DDS 中间件直接共享同一块内存区域。应用程序将数据构造在 DDS 可以“直接访问”的内存中,DDS 发送时无需将其复制到自己的内部缓冲区。
- 订阅端,DDS 将接收到的数据直接反序列化到一块应用程序可以安全访问的内存中,或者直接将指向接收缓冲区的指针/引用交给应用程序,避免复制到中间样本池再复制给应用。
- 内存池/缓冲区管理
- DDS 实现预分配和管理一组固定大小或可变大小的内存块(内存池)。
- 发布端:应用程序向 DDS 请求 一块可用的内存(
Loan)来构造数据。数据直接写在这块 DDS 管理的内存中。调用write()时,DDS 知道这块内存的位置和内容,无需复制即可直接发送。 - 订阅端:当数据到达,DDS 将其反序列化到另一块预分配的内存块中。当应用程序调用
take()时,DDS 将指向这块内存的指针/引用(以及数据所有权)交给应用程序。应用程序使用完毕后,将内存块 归还 (Return Loan) 给 DDS 内存池以供重用。
- 序列化/反序列化优化
- 使用高效的、与平台字节序匹配的序列化格式(如 CDR - Common Data Representation)。
- 在支持零拷贝的场景下,序列化/反序列化过程可能直接在共享内存块上进行原位操作,避免中间缓冲。
- 操作系统与网络栈支持
- 利用
sendfile、splice等系统调用减少内核空间到用户空间的拷贝(但在通用 DDS 中应用受限)。 - 利用 RDMA (如 InfiniBand, RoCE, iWARP) 技术实现真正的网络零拷贝(应用内存直接到远端应用内存,绕过操作系统内核和 CPU),但这通常需要特定的硬件和网络配置,不是所有 DDS 实现都原生支持或默认启用。
- 利用
二、 零拷贝的实现 (以常见 DDS 实现为例)
主要涉及关键的 API 和内存管理概念:
-
FooDataWriter的loan_sample()/get_loan()/write()with Loan:// C++ 示例 (RTI Connext) MyDataType* instance = nullptr; DDS_ReturnCode_t retcode

最低0.47元/天 解锁文章
1604

被折叠的 条评论
为什么被折叠?



