乐趣区

关于后端:Volo-开源一周年-性能优化与生态建设

回顾过去一年 CloudWeGo Rust Team 所做的工作,如果要用两个关键词来总结一下的话,那就是 性能 优化和 生态 建设。本文次要分为三点来介绍,第一点是大抵总结和回顾下 Volo 这一年的倒退,第二点是重点介绍一下 Volo 里的性能优化,第三点是将来咱们的工作重点将聚焦在哪些方面。

1. Volo 这一年

在 2022 年 8 月,咱们曾发表了一篇名为「国内首个基于 Rust 语言的 RPC 框架 — Volo 正式开源」的官宣文章,在外面具体介绍了 Volo 的一些个性以及围绕 Volo 所同步开源的 Pilota、Motore、Metainfo 等组件。时隔一年,Volo 及其相干组件已有不少变更,简略来总结一下的话,那将会别离是 Volo – 性能补全 & 性能优化、Pilota – 能力降级、Motore – 趋于稳定、Metainfo – 易于应用。

特地的,咱们还想带大家一起回顾下 Volo 在这一年中的一些要害节点和技术更新。

  • 在开源后的不久,咱们就收到了来自社区同学 @anwentec 的第一个 PR,该 PR 次要是反对用户在 Windows 上也能应用 Volo 来进行开发,大大补全了框架的多平台支持性。
  • 紧接着,咱们迎来了自公布以来的第一个重大性能优化 —— 编解码重构,该优化灵感最后起源于社区同学 @ii64 在 Pilota 中提出的一个新增 Thrift 协定反对的 PR,在一些探讨交换过后,咱们发现 Volo 现有能力不能很好的反对用户传入自定义的编解码,于是才有了当初的 Volo 编解码重构和优化。
  • 值得一提的是,在这个过程中,咱们还公布了 linkedbytes 和 faststr 这两个 crate,在辅助优化进行的同时,也丰盛了 Rust 开源的相干生态。
  • 最初,在编解码这块,咱们还通过 unsafe 代码绕过了一些边界查看,使得辅助编译器生成了更高效的 SIMD 并行操作指令,大大晋升性能。如果大家还想理解 Volo 更多具体的停顿,能够在 CloudWeGo 官网查看咱们的 Release Note。

2. 性能优化

在 RPC 框架中,最耗费性能的无非就是 序列化 网络通信 这两点,咱们做性能优化也次要是围绕这两点进行。下图展现了一次残缺的 RPC 调用链路,咱们优化的工作也根本集中在这些 CPU 密集型和 IO 密集型工作中,上面要具体介绍的编解码重构优化和 unsafe 编解码优化则次要是集中在序列化 encode、decode 局部,大家如果想参加进来一起做深度性能优化,那么这将会是一个不错的参考。

2.1 编解码重构优化

这一块的优化次要是内存的零拷贝操作。咱们晓得,在 RPC 调用时,须要先将用户申请构造体序列化成二进制字节流存入用户态内存,再通过 write 零碎调用写入到内核态内存中进行发送,咱们所优化的零拷贝局部就是在第一步存入用户态内存中。在大部分的实现中,对于 String 和 Vec<u8> 这些类型的序列化,在这里都会有一个拷贝的开销,因为在 write 零碎调用中所写入的须要是一块间断的内存。那么问题来了,如果不要求是间断内存写入,是不是就能够省掉这里的拷贝了?答案是不言而喻的能够,咱们齐全能够通过复用用户申请构造体中的内存,而后再以链表的模式把内存串起来进行写入,就能够省下这儿的拷贝开销了。

如果要对内存进行复用,那就不可避免须要引入援用计数,用来决定这块内存何时能力被开释。如此一来,原有的 String 和 Vec<u8> 这两个类型就不满足需要了,咱们须要相似 Arc<String> 和 Arc<Vec<u8>> 这样的类型。所幸的是,对于 Vec<u8>,在开源社区中曾经有 bytes 这个库中的 Bytes 构造能够进行代替。但 String 却没有一个很好的代替,而这也是 faststr 这个库诞生的一个起因。

faststr 这个库次要是提供了一个 FastStr 构造,构造外面的示意如下图,实际上它就是一个各种字符串类型的汇合,用户在应用的时候能够缩小许多如何抉择字符串类型的心智累赘。它除了满足下面对于复用内存的需要外,还对小字符串有肯定的优化,比方间接在栈上分配内存。当然,这儿有人会有疑难了,&str 不是就能够满足需要了吗,为什么还须要 faststr?其实不然,在某些场景下咱们无奈表白出它的生命周期,这一部分具体的解释大家能够看 faststr 的文档。

linkedbytes 这个库则次要是借鉴链表的思维,把咱们下面复用的 String 和 Vec<u8> 内存通过 writev 零碎调用进行写入即可。LinkedBytes 里次要是两块,一块是暂存非 String 和 Vec<u8> 内存的 bytes 字段,另一块则是把内存串起来的 list 字段。能够简略看一下 insert 的逻辑,在插入 Bytes 时,先把以后暂存的间断内存 split 进去插入到 list 中,再把传入的 Bytes 插入进去就实现了。

2.2 Unsafe 编解码优化

这一块的优化则次要是辅助编译器帮忙咱们生成进去高效的汇编代码。以 encode 为例,失常状况下咱们给内存中写入 Vec<i64>,会很容易写出如下图的代码,也就是间接遍历这个 Vec,而后调用 put_i64() 办法去写入。

但如果咱们再去认真看 put_i64() 办法的实现,就会发现每次在写入时它会先判断内存够不够用,不够的话则会裁减内存再写入。那如果一开始咱们就曾经调配了足够多的内存,是不是就齐全能够省掉这儿的边界查看,于是乎咱们能够略微革新一下,写出如下图的代码。

写完代码后那紧接着就是性能测试了,简略写个 bench 跑一下,不跑不晓得,一跑吓一跳。可能你当初也和咱们之前所认为的一样,只是去掉边界查看,性能晋升应该不会有多少,但实际上看下图,它居然有 7~8 倍的收益。

那么这就不得不好好深究一下为什么了?常言说得好,要想优化做的深,汇编代码跑不了。咱们把二者都转换成汇编代码再来看,上面两张图截取了写入过程的局部汇编代码。

看完我置信相熟 SIMD 指令的同学曾经幡然醒悟了,在去掉边界查看后的汇编代码中,采纳了 SIMD 指令去减速内存的写入,也就是一条指令能够写入多个数据,其性能收益也显得正当起来了。

3. 将来瞻望

最初给大家剧透一下咱们目前正在尝试的一些我的项目以及后续要重点优化的局部。

1. 新的我的项目

第一个是 Shmipc-rs 这个我的项目,相熟 CloudWeGo 的同学可能晓得 Shmipc 当初曾经是开源我的项目了,不过那只是 Spec 和 Go 语言版本的实现,Shmipc-rs 则是 Rust 语言版本的实现,届时也会集成到 Volo 中用以晋升性能。Shmipc 是基于共享内存的过程间通信,次要实用于大包、高吞吐的场景。

第二个是 Volo-http 这个我的项目,提供了和社区宽泛应用的 Axum 框架统一的开发体验,并且在中间件局部是基于咱们本人开源的 Motore 进行实现的,会带来一部分的性能晋升,在日后也无望和 Volo-gRPC 我的项目联合起来提供 Gateway 等性能。目前曾经是可用状态,十分欢送大家来体验和共建。

2. 易用性优化

第一个是文档这一部分,目前 Volo 中的性能个性比拟多,但大部分都短少一些文档加以阐明,以至于用户不能很好的进行应用和体验,后续咱们会把文档补充类的工作放在 issue 中进行跟进,也欢送大家参加进来。

第二个是最佳实际这一部分,目前 Volo 中只有一些繁难的 example demo 供用户学习应用,而没有一些中小我的项目给到用户去借鉴以及了解 Volo 框架,这一块也是后续要增强的局部,如果大家有什么我的项目举荐实现的话,也欢送开 issue 一起探讨。

以上是在 CloudWeGo 两周年之际,对于 Volo 开源一周年的回顾和瞻望,心愿对大家有帮忙。

我的项目地址

GitHub:https://github.com/cloudwego 

官网:www.cloudwego.io

退出移动版