一个库的命运啊,当然要靠自我奋斗,但是也要考虑到项目的实际需求,有舍才有得。
内部项目有一个基础依赖的结果「下文简述为 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