查看: 133|回复: 19

史上最快的C++ JSON库

[复制链接]

2

主题

8

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2023-1-17 21:28:57 | 显示全部楼层 |阅读模式
jsonc库通过将json格式压缩为二进制来提高性能。我当时想性能肯定秒杀其他json库,但还是我太傻太年轻。我很快就发现了不对劲的地方。
我简单的对比了一下二进制和文本解析性能,发现两者之间差距没有想象的那么大,只有很少的性能提升。因为时间主要花在内存分配上,而不是解析本身。如果要优化性能,可能需要自定义分配器。这玩意儿继续优化下去,完全失去了代码量少的优势。虽然我没有和Rapidjson对比过性能,但自己吹的牛逼,硬着头皮也要想办法。
于是我又有了一个新的想法。之前的格式都是以压缩率最大化来设计的,根本没有考虑到解析的性能。如果能保持运行时内存和持久化内存同构,就能实现解析性能的最大化。我很快设计并实现了第二版的格式,实现了零开销的解析。
两个版本和JSON文本格式的性能对比:
格式压缩率解析开销
version 120%50%
version 290%0
第一个版本,压缩率最大化,解析耗时是原来的50%。第二个版本性能最大化,和原来的大小差不多,解析耗时是0。
为什么时间是0呢,解析时其实只做了一次指针的跳转,代码像这样:
    static Value *decode(char *buffer, int size) {
        int32_t *jmp = (int32_t*)buffer;
        if (*jmp > size) return NULL;
        return (Value*)(buffer + *jmp);
    }其实这个跳转也能想办法省略,但这样已经是0耗时了。jsonc中两个版本同时支持,根据自己的需求选择。
其他优势

jsonc既有文本格式的优点,却没有json的缺点。对json只是压缩,不改变语义。除了压缩率和解析性能以外,这么做的好处:

  • 1.很容易在二进制和json格式之间互转,而不产生潜在bug。例如游戏在开发阶段使用文本格式,发布时使用二进制格式来提高性能,格式切换后并不需要做大量可靠性测试。
  • 2.在代码中使用相同的数据接口,这让两种序列化方式共享相同代码。你可以通过请求参数来控制服务端返回文本格式还是二进制格式,同时支持两种格式不会增加代码量。
  • 3.即使服务端不用双格式支持,对二进制格式有任何debug需求,也可以通过自带的工具将二进制转成文本格式。不需要schema接口定义文件。对可读性伤害较小。
  • 4.即使维护旧服务,不想动旧代码。也可以在代理服务上包一层,将文本json转成二进制格式。这样能提高客户端性能,和节省用户流量。
源码见这里:
回复

使用道具 举报

3

主题

6

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2023-1-17 21:29:41 | 显示全部楼层
二进制存储的json还叫json吗,json本来就是强调一个human readable。bson和flatbuffers也是内存和持久化同构,而且flatbuffers通过生成的头文件访问能达到O(1)的访问时间复杂度。(而且bson和flatbuffers也可以转换json格式输出)
回复

使用道具 举报

0

主题

7

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2023-1-17 21:29:54 | 显示全部楼层
我看了你说的bson和flatbuffers。bson是独立格式,自定义了很多数据类型。bson文件很大,不但没压缩,反而文件更大了。bson并没有实现零开销解析,可能你对内存同构有什么误解。

flatbuffers倒是实现了零开销的解析,但是它更像Protocol Buffers。需要一个schema接口定义文件,然后用工具生成解析代码。完全和json不同的思路。

目前来说jsonc还没有竞品。
回复

使用道具 举报

2

主题

5

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2023-1-17 21:30:48 | 显示全部楼层
bson的解析就是将文件里面内容读取出来,除了bson的顶层Object没有存储头部5字节的length和type以外,只是在读取buffer的时候用获得的文件长度构建了一个5字节的头部而已,本质来说是还是同构的
回复

使用道具 举报

3

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2023-1-17 21:31:11 | 显示全部楼层
序列化和反序列化的时候,可以把bson里面的buffer取出来,手动存成文件,然后用再用fstream读出来一个数组,用init_from_data读出来。你可以看看init_from_data的实现,就是构建了一个顶层Object的头部,buffer在内存和文件中布局是一致的
回复

使用道具 举报

2

主题

6

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2023-1-17 21:32:03 | 显示全部楼层
好吧,我说的是全局同构,你说的是单个结点同构。我就不应该提同构这个词,零开销才是重点。我也在正文中补充了一些除了压缩率和解析性能以外,jsonc的好处,这些都是bson所不具备的。
回复

使用道具 举报

2

主题

6

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2023-1-17 21:32:19 | 显示全部楼层
全局也是同构的,嵌套的结构体会in-place存储,bson的所有内容都位于连续的内存中
回复

使用道具 举报

2

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2023-1-17 21:33:10 | 显示全部楼层
我搜到的结果bson的解析耗时是json的70%。要真能零开销解析,官网早就吹起来了。多说无意,拿出你的测试结果。
回复

使用道具 举报

5

主题

8

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2023-1-17 21:33:17 | 显示全部楼层
你去看看人家的代码实现不就行了,还需要我给benchmark?我们项目就在用这个,有什么好吹的,做技术的不要搞的和销售一样
回复

使用道具 举报

5

主题

8

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2023-1-17 21:33:49 | 显示全部楼层
现在都不是最快的,还史上[惊喜]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表