You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Protobuf文件在R中反序列化后内存占用大幅降低?

问题:Protobuf序列化前后体积巨大差异的原因

我调用某API下载了一个包含多条消息的大型Protobuf文件(约2.1GB),使用DecodeVarint32函数反序列化后,得到的R对象体积却极小(不足30MB),且所有消息均已正确反序列化,数据完整。反序列化前httr请求内容的大小为2.1GB,反序列化后R对象大小仅1.5MB。

【补充示例】:子集数据可复现该情况:rg对象大小为1.1MB,反序列化后的outputlist(含两条消息)仅1904字节。复现代码及DecodeVarint32函数如下:

复现代码

rg = readRDS("rg.RDS")

library(dplyr)
library(RProtoBuf)
readProtoFiles("message2.proto", package = 'RProtoBuf')

alldata = rg$content
n = 1
i = 1
outputlist = list()
while (n < length(alldata)) {
  pos = DecodeVarint32(alldata,as.integer(n))
  clen = pos[[1]]
  n = pos[[2]]
  nend = n + clen - 1
  tmp = alldata[n:nend]
  outputlist[[i]] = read(Spectrum, tmp)
  n = nend + 1
  i = i+1
}
object.size(outputlist)

DecodeVarint32函数代码

library(bitops)

DecodeVarint32 = function(buffer, pos){
  mask = (2 ^ 32 - 1)
  result = 0 %>% as.raw
  shift = 0
  while(TRUE){
    b = buffer[pos]
    result = bitOr(as.numeric(result),
                   bitShiftL(
                     bitAnd(as.numeric(b), as.numeric(0x7f)),
                     shift))
    pos = pos + 1
    if(!bitAnd(b,0x80)){
      result = bitAnd(result,mask)
      result = as.integer(result)
      return(list(result, pos))
    }
    shift = shift + 7
    if(shift >= 64){
      stop("Too  many bytes when decoding varint")
    }
    
  }
}

原因解释

这种巨大的体积差异主要来自Protobuf二进制格式与R内存对象的存储特性差异,具体可以从以下几点分析:

  • Protobuf二进制的元数据开销:Protobuf的每个字段都需要携带字段编号(varint编码)和类型标识,这些元数据会在每条消息中重复存储。如果响应包含大量结构相同的消息,这些重复的元数据会累积出巨大的体积。而反序列化后的R对象是结构化的列表/对象,字段结构由.proto文件定义,不需要重复存储字段编号等元信息,只保留实际数据值,因此节省了大量空间。

  • 重复数据的内存共享:R的内存管理机制会自动共享重复的数据对象。如果Protobuf响应中存在大量重复的内容(比如多条消息包含相同的字符串、数值数组或子消息),Protobuf的二进制流会为每条消息独立存储这些数据,而R会将重复数据只存储一次,通过引用方式访问,大幅降低内存占用。这也是示例中1.1MB的二进制数据反序列化后仅1.9KB的核心原因之一。

  • 数据编码的存储效率差异:

    • Protobuf使用varint编码整数,对于较大的整数(超过2^28),varint需要占用5字节存储,而R中整数通常以固定4字节的原生格式存储,单值占用空间更小;
    • 对于重复字段,Protobuf需要为每个元素单独编码(包括字段标识),而R会将重复字段存储为紧凑的向量结构,不需要为每个元素附加元数据,存储效率更高。
  • 无意义填充或冗余数据:如果API返回的Protobuf响应中包含未使用的填充字节、预留字段或重复的空值字段(尽管Protobuf默认不存储默认值,但某些实现可能会额外写入),这些内容会增加二进制体积,但反序列化后不会被保留为有效数据,进一步拉大体积差。

内容的提问来源于stack exchange,提问作者Julien Sade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:50:51