0

空间优先的 protobuffer / json 解码器

 1 year ago
source link: https://blog.codingnow.com/2022/08/memory_compat_protobuffer_json_unmarshaling.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

空间优先的 protobuffer / json 解码器

今天同事给我转了一个帖子 ,说的是 golang 在处理大量并发 json / protobuffer unmarshaling 事务时,可能产生大量的 (10GB) 临时内存,无法及时回收的问题。

我的观点是:如果一个系统的某个模块有可能使用 10G 这个量级的内存,那么它必然是一个核心问题需要专门对待。核心问题应该有核心问题的考量方法,这不是 GC 之错,也并非手动管理内存一条解决之道。即使手工管理内存,也无非是把内存块之管理转嫁到一个你平常不太想关心的“堆”这个数据结构上,期待有人实现了一个通用方案尽可能的帮你解决好。如果随意使用,一样有类似内存碎片无法合并之类的问题,吃掉你额外的内存。如果它存在于你的核心模块,你一样需要谨慎考量。

在这个具体问题上,我认为还是应该先简化数据结构,让核心数据结构变得易管理。显然、固定大小的数据结构是最容易被管理的。因为如果你的数据结构大小固定,就不再需要堆管理,而是一个固定数组滚动处理临时数据。

json 这样的弹性数据结构看起来不太好用固定大小的数据结构去描述,但稍微想点办法也不难做到。C 在这方面的库不少,我就不一一列举。核心想法一般是:用一个固定数据结构去引用编码数据中的切片。解码后的结构只保留元素的类型以及在原始数据中的位置。如果你能大致知道需要解码的数据元素的数量级,那么就可以用一个固定大小的结构去承载解码结果,不需要任何动态的内存分配。

只不过大多数库把目标都放在时间因素上,让解码更快;少放在空间因素上,让解码过程少使用内存;所以这样的解码方式并不常用。如果你的核心问题于此,你就得去考虑这样的方案。

protobufffer 会比 json 的结构更适合这样做。因为它已经把数据的 key 部分转换成数组 id ,且整体数据结构是明确的。但只是因为 protobuffer 编码更繁杂,不如json 那么简单,反而相关的库更少。


第二、如果你真的有这样的需求:要解码成千上万的数据结构类似的数据包。就应该考虑下列模式:

假设你要解码的数据块的类型是 X ,可以预先生成一个叫做 X 的 accessor 访问器。当你的业务逻辑需要访问 X.a.b 这个字段的时候,应该去调用 X.a.b(binary) ,此处的 binary 指编码过的 json/protobuffer 数据块,X.a.b() 是一个预生成好的访问器函数,它可以用最优化的方法从数据块中提取出需要的数据。

我们还可以进一步的为 X 做一个索引函数,可以预处理 binary ,制作一个固定内存大小的索引信息结构,为具体字段的访问加速。

这个 accessor 对象不太在乎多复杂,因为它可能在整个程序中只初始化一次。

云风 提交于 August 12, 2022 11:40 AM | 固定链接


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK