Protocol Buffers 中实现ZeroCopyStream 来减少内存拷贝

来源:互联网 发布:电气画图用什么软件 编辑:程序博客网 时间:2024/06/02 16:16
Protocol Buffers 中实现ZeroCopyStream 来减少内存拷贝

我们在序列化、反序列化 Protobuf message 时为了最小化内存拷贝,可以实现其提供的 ZeroCopyStream(包括 ZeroCopyOutputStream 和 ZeroCopyInputStream)接口类,ZeroCopyStream 要求能够进行 buffer 的分配,这体现在一个名为 Next 的接口上,这样做的好处是避免进行内存的拷贝,为了方便理解,我们来看一下 ZeroCopyInputStream 和传统的 stream 的对比:

  1. // 典型的做法,我们调用 input stream 的 Read 从内存中读取数据到 buffer
  2. // 这里进行了一次拷贝操作,也就是拷贝内存中的数据到 buffer 中
  3. // 之后 DoSomething 才能处理此数据
  4. char buffer[BUFFER_SIZE];
  5. input->Read(buffer, BUFFER_SIZE);
  6. DoSomething(buffer, BUFFER_SIZE);
  7.  
  8. // 使用 Next 接口的做法,input stream 内部有责任提供(分配)buffer
  9. // 也就是说,DoSomething 可以直接操作内部的内存,而无需拷贝后再操作
  10. // 这就避免了一次内存拷贝
  11. const void* buffer;
  12. int size;
  13. input->Next(&buffer, &size);
  14. DoSomething(buffer, size);

更多的内容可以参考:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream

本文探讨一下 ZeroCopyOutputStream(ZeroCopyInputStream 类似,就不详细说明了)。ZeroCopyOutputStream 接口类有以下几个接口需要实现:

  1. // 获得(分配)一个用于写入数据的 Buffer 给调用者
  2. virtual bool Next(void ** data, int * size) = 0;
  3.  
  4. // 由于调用 Next 请求 stream 分配了一块 Buffer 给调用者使用
  5. // 在最后一次 Next 调用时可能分配的 Buffer 大小大于需要的大小
  6. // BackUp 接口用于归还多余的内存
  7. virtual void BackUp(int count) = 0;
  8.  
  9. // 返回总的被写入的字节数
  10. virtual int64 ByteCount() const = 0;

Next 接受两个参数 data 和 size,data 和 size 保证不为 NULL,data 用于获取 Buffer 的地址,size 用于获取 Buffer 的长度。此函数被调用意在获取一个可以用来写入数据的连续的 Buffer。函数返回 false 表示调用失败。
BackUp 的意义在于,调用了 Next 之后获取到一块大小为 size 的 Buffer 用于写入数据,但有可能出现数据全部写入完了 Buffer 还未使用完,这时候需要调用 BackUp 把未使用的内存归还。

https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyOutputStream 详细的描述了每个接口的前置和后置条件。在 Protobuf 中已经包含多个 ZeroCopyStream 的实现,例如:ArrayOutputStream,它们都可以作为我们实现 ZeroCopyStream 的范例。


0 0
原创粉丝点击