写点什么

高性能 Rust JSON 库 sonic-rs 优化实践

  • 2024-03-26
    北京
  • 本文字数:2008 字

    阅读完需:约 7 分钟

大小:904.33K时长:05:08
高性能 Rust JSON 库 sonic-rs优化实践

一、sonic-rs 介绍

sonic-rs 是一个基于 SIMD 的高性能 Rust JSON 库,是 sonic JSON 库的 Rust 版本。


字节跳动 sonic 开源项目如今包含了不同语言的多个 JSON 库(如下)。其中,sonic-go 最先开源,使用了 JIT 和 SIMD 技术,sonic-cpp 使用了 C++ 模板和 SIMD 技术,这两个 JSON 库均已经在字节内部得到了较大规模的落地。在成本优化大背景下,为了帮助 Golang 业务迁移 Rust,优化 Rust JSON 性能,我们基于 JSON 方面的优化经验和实践,用纯 Rust 语言开发了高性能的 JSON 库  sonic-rs。


  • sonic(Golang JSON 库):https://github.com/bytedance/sonic

  • sonic-cpp(C++ JSON 库):https://github.com/bytedance/sonic-cpp

  • sonic-rs(Rust JSON 库):https://github.com/cloudwego/sonic-rs


sonic-rs 目前支持的 JSON 功能比较齐全,基本对齐了 serde-json 的相关功能,并且提供更加丰富的功能和更多的高性能接口。sonic-rs 的主要功能特点有:


  • 基本兼容 Serde 生态,同时支持 Volo 中的 FastStr 类型

  • 支持动态类型编解码和按需解析

  • 支持 LazyVaue,RawNumber 等类型

  • 支持 UTF-8 校验和标准浮点数精度


在性能方面,我们基于 serde-rs 官方 benchmark (https://github.com/serde-rs/json-benchmark) 提供的 Rust 结构体和 JSON 数据,对 serde-json, simd-json 和 sonic-rs 在 Rust 结构体下的解析性能进行了测试,可以发现 sonic-rs 的性能是 simd-json 的 1.5~2 倍,是 serde-json 2 倍:


二、sonic-rs 优化实践


sonic-rs 的优化主要基于 SIMD,其中部分借鉴了其他 JSON 库,如 simd-json 的优化思路。SIMD (Single instruction, multiple data) 是一种并行优化技术,可以用一条指令,并行处理多个数据。如今大多数 CPU 已经支持了各种 SIMD 指令集。例如,x86_64 架构下的 SSE,AVX2,AVX512, aarch64 架构下的 neon 指令集等。使用 SIMD 指令优化之后,对于合适的任务,程序执行的指令数量会更少,因此性能会更好。


在整体设计上,sonic-rs 并没有采用 simd-json 那种二阶段解析的思路,主要将 SIMD 优化应用于 JSON 解析和序列化中的热点,包括字符串序列化、按需解析和浮点数解析等。

1. SIMD 优化字符串序列化


字符串序列化是 JSON 序列化的热点。序列化时,需要扫描字符串中的转义字符。对于较长的字符串,逐个字节判断转义字符的操作是比较耗时的,扫描转义字符非常适合使用 SIMD 来加速。


如果用 AVX2 指令来扫描转义字符,如下面代码所示。这段 SIMD 代码在 haswell 架构下面,开 O3 优化之后,其实只有六条 SIMD 指令,即 6 条 SIMD 指令可以一次性扫描 32 个字节。相比较标量代码来说,大大减少了程序指令的数量,从而减少了程序的执行时间。


2. SIMD 优化按需解析


很多业务场景只用到 JSON 中的部分字段,很适合按需解析,在解析时跳过不需要的 JSON 字段。在跳过 JSON 字段时,难点在于如何高效跳过 JSON 中的 object 和 array。


基于 JSON 中 object 和 array 括号必须匹配的语法规则,sonic-rs 使用 SIMD 实现了高效的括号匹配算法。先通过 SIMD 得到 json object 和 array 的 bitmap,然后通过计算括号的数量来跳过 object 和 array。当发现括号匹配时,就可以跳过该 object 或 array。


3. SIMD 优化浮点数解析


浮点数解析是 JSON 解析中的一个性能热点。在字节内部,我们发现 JSON 中大部分浮点数的尾数都比较长,也适合使用 SIMD 优化。如下图,对于一段长 16 个字节的浮点数尾数 "1234342112345678":


  • 先将这段字符串读取到向量寄存器里面,此时向量的每个数字还是 ASCII 码的值。

  • 其次,用向量的减法,逐个字节减去 ASCII 码 '0' 得到 v1。这时。v1 里面的数字已经是十进制。

  • 然后,继续对 v1 里面的各个数字用向量指令做两两乘加(高位乘以 10 再加上低位),得到 v2。v2 里面的各个数已经是十进制的两位数。

  • 以此类推,利用 SIMD 指令逐层累加,最终就得到 v16。v16 里面是一个 16 位数,即最终的尾数解析结果。

  • 最后,我们再用向量指令把 v16 转成 u64 类型。


整个解析过程,不用遍历浮点数尾数的每一个字符,就能完成浮点数尾数解析。


三、sonic-rs 现状和规划


sonic-rs 已经开源几个月了,目前迭代到了 0.3 版本,已经支持 Rust stable 版本,并且支持了 aarch64 架构。sonic-rs 沉淀了一些使用文档,用以帮助各方面的开发者:


  • Golang 迁移 Rust 用户使用 sonic-rs:https://github.com/cloudwego/sonic-rs/blob/main/docs/for_Golang_user_zh.md

  • Rust serde-json 用户迁移 sonic-rs:https://github.com/cloudwego/sonic-rs/blob/main/docs/serdejson_compatibility.md

  • 性能优化细节:https://github.com/cloudwego/sonic-rs/blob/main/docs/performance_zh.md


后续,sonic-rs 会在性能,易用性和稳定性上面继续打磨,预期会支持对 Bytes/FastStr 等常见数据类型的零拷贝解析,支持运动时检测 SIMD 指令等,欢迎感兴趣的开发者一起加入我们。


项目地址

GitHub:https://github.com/cloudwego

官网:www.cloudwego.io

2024-03-26 17:073929

评论

发布
暂无评论
发现更多内容

智能商业时代的思考(一)从在线化到网络化

刘旭东

拼多多 淘宝 智能商业 网络协同

宁波新基建之路 基于制造优势破题智慧发展

CECBC

新基建

一个在交流群里讨论过两轮的问题,答案竟然跟一个 PEP 有关

Python猫

Python 编程

Python 为什么能支持任意的真值判断?

Python猫

Python 编程

区块链技术应用于链接智慧医疗

CECBC

区块链 社会保险 智能医疗

怎么向女朋友解释什么叫区块链?

艾小仙

比特币 区块链 以太坊 defi

区块链技术破解数字版权保护难题

CECBC

区块链 版权保护 数字技术

妈妈,今天您几点下班?

脑极体

拥抱K8S系列-04-基于docker部署更多应用

张无忌

Docker 标准化 vsftpd

Python 为什么没有 void 关键字?

Python猫

Python 编程

oeasy教您玩转linux010206toilet

o

人生革命由自律发起

胡迪伦

自学编程 拖延症 懒惰 死循环

SpringBoot 缓存之常用注解

hepingfly

Java 缓存 springboot 注解

Flink从保存点启动应用-18

小知识点

scala 大数据 flink

Python 中的数字到底是什么?

Python猫

Python 翻译 PEP

商业通识 : 商业到底是什么?

Walker

学习 得到 个人成长 商业

首个数字银行卡明年发行,广州出台区块链措施支持大湾区

CECBC

区块链 金融科技 社会

如何将VSCode变成绿色版本

lmymirror

vscode 教程

职场求生攻略答疑篇之 3 —— 数据是土地

臧萌

数据 职场成长

Elasticsearch之mapping

北漂码农有话说

为稳外贸保驾护航 区块链交易平台显身手

CECBC

区块链 银行 福费廷

JavaScript 深拷贝与浅拷贝

梁凤波

Google鼓励的13条代码审查标准 [建议收藏]

简爱W

【MySQL】我这样分析MySQL中的事务,面试官对我刮目相看!!

冰河

MySQL 面试 事务 隔离级别 冰河

java安全编码指南之:声明和初始化

程序那些事

安全编码 java安全编码 编码指南 对象初始化

2020-09-03-第十三周作业

路易斯李李李

Python 为什么要在 18 年前引入布尔类型?且与 C、C++ 和 Java 都不同?

Python猫

Python 编程

持续集成有什么好处?快来看鸭

清菡软件测试

jenkins

[翻译]Defer,Panic,and Recover

卓丁

defer panic recover Go 语言

Python 函数为什么会默认返回 None?

Python猫

Python 编程

为什么Java二维数组不用指定列的长度

Rayjun

Java 数组

高性能 Rust JSON 库 sonic-rs优化实践_编程语言_刘强_InfoQ精选文章