一个库的命运啊,当然要靠自我奋斗,但是也要考虑到项目的实际需求,有舍才有得。
内部项目有一个基础依赖的结果「下文简述为 origin.json.gz」是json.dump
然后压缩成gz
文件存储的,下游引用的时候有个头大的问题就是这玩意太大了,动辄百兆级别,一般的json.load
就很慢,于是就一直在寻找可用的性能更为优秀的方法来替换掉内置json
处理,至少期望是load
快一些。
先后尝试过不同的json
库,包括不限于以下选手:orjson/ujson/rapidjson/simplejson
,基本的对比效果就是:序列化各种吊打内置库,但反序列化基本上没啥太大的优势,直到有一天老板甩了个分享链接:Faster, more memory-efficient Python JSON parsing with msgspec (pythonspeed.com)
发现msgspec
这玩意简直强的离谱:提前定义好struct
然后按需streaming
加载的思路性能极佳且内存极度友好。
简单说明一下这个origin.json.gz
的大致结构:
|
|
某个下游引用的场景是:需要origin.json.gz
里的某个节点的部分信息来做一些二次处理的事情。以往的操作不管如何先load
一把,实在浪费资源。如果用msgspec
来处理,是这样的姿势:
|
|
代码量看起来是比以前一把梭哈json.load
多了一点,但收益巨大:同样的硬件条件,使用msgspec.decode
快了近一个数量级。
虽然没有去翻源码去看具体实现,但二进制的世界没有魔法,无非就是在玩时间空间的把戏。msgspec.decode
的快源于两点:
- 预定义了数据类型,他的核心解析器可以节省大量不必要的类型判断
- 按需定义,忽略不必要的数据
这就是一个空间换时间的玩法,按需加载显著降低了需要处理的数据量,自然性能就上来了。
这时候来了个成年人说我都要行不行?很遗憾,不行。在预定义了所有节点的数据结构「去掉按需加载的 buff」以后,msgspec.decode
的速度甚至比内置json
还慢 20% (基于一坨 400MB 大小的origin.json
测试所得),object
显然要比dict
慢上一拍的。
# NOTE: I am not responsible for any expired content.
create@2022-05-25T08:12:46+08:00
update@2022-06-04T23:18:21+08:00
comment@https://github.com/ferstar/blog/issues/62