mm换算cm(mm换算成英寸)

仓库:https://github.com/bytedance/sonicSonic是字节跳动的一个开源Golang JSON库。基于即时编译和单指令多数据技术

仓库:https://github.com/bytedance/sonic

Sonic是字节跳动的一个开源Golang JSON库。基于即时编译和单指令多数据技术,大大提高了围棋程序的JSON编译性能。同时,结合lazy-load设计思想,也为不同的业务场景打造了全面高效的API。

自2021年7月发布以来,sonic已被Tik Tok、今日头条和其他企业采用,为字节跳动节省了数十万个CPU内核。

为什么要自己开发JSON库?

JSON(JavaScript Object Notation)因其简洁的语法和灵活的自描述能力,被广泛应用于各种互联网服务中。但JSON本质上是文本协议,没有类似Protobuf的强制模型约束(schema),所以编解码效率往往很低。再加上一些业务开发者对JSON库的选择和使用不当,服务性能会急剧恶化。

在字节跳动,我们也遇到了上述问题。根据公司CPU占TOP 50业务的性能分析数据,JSON编解码成本一般接近10%,单个业务占比甚至超过40%。提高JSON库的性能非常重要。因此,我们对业界现有的Go JSON库进行了一些评测测试。

首先,根据主流的JSON库API,我们把它们的用法分为三种:

泛型(generic)编解码:JSON 没有对应的 schema,只能依据自描述语义将读取到的 value 解释为对应语言的运行时对象,例如:JSON object 转化为 Go map[string]interface{};定型(binding)编解码:JSON 有对应的 schema,可以同时结合模型定义(Go struct)与 JSON 语法,将读取到的 value 绑定到对应的模型字段上去,同时完成数据解析与校验;查找(get)& 修改(set):指定某种规则的查找路径(一般是 key 与 index 的集合),获取需要的那部分 JSON value 并处理。其次,我们根据样本 JSON 的 key 数量和深度分为三个量级:小(small):400B,11 key,深度 3 层;中(medium):110KB,300+ key,深度 4 层(实际业务数据,其中有大量的嵌套 JSON string);大(large):550KB,10000+ key,深度 6 层。

测试结果如下:

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

不同数据级别下JSON库的性能

结果表明,目前这些JSON库并不能在所有场景下都保持最佳性能。即使是应用最广泛的第三方库json-iterator,在通用编解码和大规模场景下也无法满足我们的需求。

JSON库的基准编解码性能固然重要,但不同场景的最优匹配更为关键——于是我们走上了自己开发JSON库的道路。

开源图书馆的声音技术原理

由于JSON业务场景的复杂性,期望通过单一算法进行优化是不现实的。所以在设计sonic的过程中,我们借鉴了其他领域/语言(不限于JSON)的优化思路,并将其融入到所有处理环节中。有三种核心技术:JIT、延迟加载和SIMD。

吉特舞乐

对于使用模式的定型编码和解码场景,许多操作不需要在运行时执行。这里的“运行时”是指程序实际开始解析JSON数据的时间段。

例如,如果在业务模型中确定一个JSON键值必须是布尔类型,那么我们可以在序列化阶段直接输出这个对象对应的JSON值(' true '或' false '),而不需要检查这个对象的具体类型。

sonic-JIT的核心思想是将模型解释与数据处理逻辑分离,使前者固定在“编译时”。

这种思想也存在于标准库和一些第三方json库中,比如json-iterator的函数组装模式:将Go struct解释为每个字段类型的编解码器函数,然后组装缓存为整个对象对应的编解码器,然后在运行时加载处理JSON。然而,这种实现很难避免被转换成大量的接口和函数调用栈。随着JSON数据的增加,函数调用的成本成倍增加。只有真正编译模型解释逻辑,实现无栈执行器,模式带来的性能收益才能最大化。

目前业界主要有两种实现方式:代码生成code-gen(或模板template)和即时JIT编译。前者的优点是库开发者实现起来相对简单,缺点是增加了业务代码的维护成本和局限性,无法实现秒级热更新——这也是代码生成的JSON库受众不广泛的原因之一。JIT编译过程移到程序的加载(或第一次解析)阶段,只需提供JSON schema对应的结构类型信息,就可以一次性编译好相应的codec并高效执行。

sonic-JIT的一般过程如下:

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

音速JIT系统

初次运行时,基于 Go 反射来获取需要编译的 schema 信息;结合 JSON 编解码算法生成一套自定义的中间代码 OP codes;将 OP codes 翻译为 Plan9 汇编;使用第三方库 golang-asm 将 Plan 9 转为机器码;将生成的二进制码注入到内存 cache 中并封装为 go function;后续解析,直接根据 type ID (rtype.hash)从 cache 中加载对应的 codec 处理 JSON。

从最终实现的结果来看,sonic-JIT生成的编解码器性能不仅优于json-iterator,甚至超过了easyjson(详见后面的“性能测试”)。这一方面与底层文本处理操作符的优化有关(见下文“SIMD & asm2asm”一章),另一方面sonic-JIT可以控制底层CPU指令,在运行时建立独立高效的ABI(应用二进制接口)系统:

将使用频繁的变量放到固定的寄存器上(如 JSON buffer、结构体指针),尽量避免 memory load & store;自己维护变量栈(内存池),避免 Go 函数栈扩展;自动生成跳转表,加速 generic decoding 的分支跳转;使用寄存器传递参数(当前 Go Assembly 并未支持,见“SIMD & asm2asm”章节)。

惰性负载

对于大多数Go JSON库来说,通用编解码器是性能最差的场景之一。但由于业务本身的需要或者业务开发者选择不当,往往是最常使用的场景。

通用编解码器性能差只是因为没有图式吗?其实不是,我们可以对比C++的JSON库,比如rappidjson和simdjson。他们的解析方法一般,但是性能还是很不错的(simdjson可以达到2GB/s以上)。标准库泛型解析性能差的基本原因是它使用Go native generics-interface(map[string]interface { })作为JSON的编解码对象。

其实这是一个不好的选择:首先,在数据反序列化的过程中,地图插入的成本非常高;其次,在数据序列化的过程中,映射遍历的效率远不如数组遍历。

回过头来看,JSON本身就有完整的自我描述能力。如果用一种更接近JSON AST的数据结构来描述,不仅可以使转换过程更简单,还可以实现lazy-load——这是sonic-ast的核心逻辑:它是Go中的一个JSON codec对象,用node {type,Length,pointer}表示任意JSON数据节点,结合树和数组结构描述节点之间的层次关系。

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

sonic-ast的结构示意图

Sonic-ast实现了一个有状态的、可扩展的json解析过程:当用户获取一个密钥时,Sonic使用skip calculation来淡化待获取密钥前的JSON文本;对于key之后的JSON节点,直接不做解析;只有用户真正需要的键才被完全解析(转换成某种Go原语类型)。因为节点转换的成本远低于解析JSON的成本,所以在不需要完整数据的业务场景中,好处是相当可观的。

虽然skip是轻量级的文本解析(处理JSON控制字符“[”、“{”等。),在使用gjson这样的纯JSON查找库时,往往会出现相同路径查找导致的重复开销。

为了解决这个问题,sonic在子节点的skip处理中增加了一个步骤,记录被skip的JSON的key、start位和end位,并分配一个Raw-JSON类型的节点来保存,这样就可以基于节点的偏移量直接进行二次skip。同时,sonic-ast支持节点的更新、插入和序列化,甚至支持将任何Go类型变成节点并保存。

换句话说,sonic-ast可以作为一个通用的数据容器来代替Go接口,在协议转换、动态代理等服务场景中有很大的潜力。

SIMD和asm2asm

无论是定型编解码场景,还是通用编解码场景,核心都离不开对JSON文本的处理和计算。对于其中的一些问题,业界已经有成熟高效的解决方案,比如浮点数转字符串算法Ryu、整数转字符串查找法等。,这些都是在sonic的底层文本操作符中实现的。

还有一些问题在逻辑上相对简单,但可能面对更大数量级的文本,比如JSON字符串的处理,跳过空白字符等。这时候就需要一些技术手段来提高处理能力。SIMD就是这样一种并行处理大规模数据的技术。目前,大多数CPU已经拥有SIMD指令集(如英特尔AVX),并且已经在simdjson中成功实践。

以下是sonic中skip 空白字符的算法代码:

#if USE_AVX2 // 一次比较比较32个字符 while (likely(nb >= 32)) { // vmovd 将单个字符转成YMM __m256i x = _mm256_load_si256 ((const void *)sp); // vpcmpeqb 比较字符,同时为了充分利用CPU 超标量特性使用4 倍循环 __m256i a = _mm256_cmpeq_epi8 (x, _mm256_set1_epi8(' ')); __m256i b = _mm256_cmpeq_epi8 (x, _mm256_set1_epi8('\t')); __m256i c = _mm256_cmpeq_epi8 (x, _mm256_set1_epi8('\n')); __m256i d = _mm256_cmpeq_epi8 (x, _mm256_set1_epi8('\r')); // vpor 融合4次结果 __m256i u = _mm256_or_si256 (a, b); __m256i v = _mm256_or_si256 (c, d); __m256i w = _mm256_or_si256 (u, v); // vpmovmskb 将比较结果按位展示 if ((ms = _mm256_movemask_epi8(w)) != -1) { _mm256_zeroupper(); // tzcnt 计算末尾零的个数N return sp - ss + __builtin_ctzll(~(uint64_t)ms); } /* move to next block */ sp += 32; nb -= 32; } /* clear upper half to avoid AVX-SSE transition penalty */ _mm256_zeroupper();#endif

strnchr()在sonic中的实现(SIMD部分)

开发者会发现,这段代码其实是用C语言写的——其实sonic中大部分的文本处理功能都是用C语言实现的:一方面,SIMD指令集用C语言封装得很好,更容易实现;另一方面,这些C代码可以通过clang编译,充分享受其编译优化带来的提升。所以我们开发了一套将x86汇编转换为Plan9汇编的工具asm2asm,通过Go汇编机制将clang输出的汇编静态嵌入sonic。同时,在JIT生成的编解码器中,我们利用ASM工具计算的C函数的PC值直接调用调用指令进行跳转,从而绕过Go汇编不能注册参数的限制,挤压最后的CPU性能。

其他的

除了上面提到的技术,sonic内部还有很多细致的优化,比如用RCU代替sync。Map提高编解码缓存的加载速度,使用内存池减少编码缓冲区的内存分配等等。由于这里篇幅有限,就不详细介绍了。有兴趣的同学可以自行搜索阅读sonic源代码。

特性试验

我们在上一篇文章中测试了不同的测试场景,结果如下:

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

小数据(400B,11个键,3层深度)

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

中等数据(110KB,300+密钥,4层深度)

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

大数据(550KB,10000多个密钥,6层深度)

可以看出,sonic在几乎所有场景下都是领先的(sonic-ast直接使用Go汇编导入的C函数,导致在小数据集下性能有所损失)

平均编码性能较 json-iterator 提升 240% ,平均解码性能较 json-iterator 提升 110% ;单 key 修改能力较 sjson 提升 75% 。

而且在生产环境中,sonic也验证了良好的回报,服务高峰期占用的内核数量减少了近三分之一:

sonic:基于 JIT 技术的开源全场景高性能 JSON 库

sonic上线前后一个字节服务的CPU使用率(内核数)比较

标签

由于底层是基于汇编开发的,sonic目前只支持amd64架构下的darwin/linux平台,未来会逐步扩展到其他操作系统和架构。此外,我们还考虑将sonic在Go语言中的成功经验移植到不同的语言和序列化协议中。目前,sonic的C++版本正在开发中,其定位是基于sonic的核心思想和底层操作符,实现一套通用的高性能JSON编解码接口。

近日,sonic发布了第一个大版本v1.0.0,这表明它可以被企业灵活地用于生产环境中,也是在积极响应社区的需求,拥抱开源生态。我们期待sonic未来在使用场景和性能上有更多突破。欢迎开发者加入,贡献PR,共同打造业界最好的JSON库!

相关链接

项目地址:https://github.com/bytedance/sonic

基准:https://github.com/bytedance/sonic/blob/main/bench.sh

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

作者:美站资讯,如若转载,请注明出处:https://www.meizw.com/n/171548.html

发表回复

登录后才能评论