数牍 X Rust,那些不得不说的事
第二届中国 Rust 开发者大会(Rust China Conf 2021—2022)即将开幕。数牍科技工程师韦晓亚将在“加密、隐私与可信计算”环节分享公司使用 Rust 构建高性能隐私计算平台心得。
韦晓亚是数牍科技多方安全计算平台的负责人之一,加入数牍之前他一直在硅谷工作,熟稔于分布式系统和大数据处理相关的领域,曾在谷歌云负责视频流的弹性存储和流式计算,在 airbnb 负责图数据库和知识图谱的工作。
在大会开始前,我们请晓亚进行一场有关“数牍与 Rust”的快问快答:我们为什么选择 Rust 语言在隐私计算中重新造了分布式计算的轮子?在进行具体的工程实践的时候遇到了哪些问题?我们爱 Rust 什么,又“痛”在哪里?
Q:我们为什么选择 Rust 语言重新造轮子?
X:这需要先简要谈谈联邦学习和多方安全计算。传统的机器学习通过将数据训练成模型后去做预测,所谓“garbage in, garbage out”,模型的质量很大程度上取决于训练样本的质量。但是现实场景中,往往某一个机构自有的数据是不足以拿来训练为一个比较好的模型,需要多家机构的数据放在一起训练模型,就需要用到联邦学习技术。
比如 A 公司和 B 公司各自持有数据,因为合法合规或数据隐私保护的原因,两者之间不能够进行数据的交互,但又确实有将数据放在一起进行模型训练的需求。这时通过联邦学习技术,两方各自先利用自己的数据训练模型,过程中一方面利用自己的数据,另外一方面在模型训练的过程中,两方进行加密通信,互相交换模型训练的中间结果,而且根据这些中间结果是无法反推出原数据的。完成上述模型训练后,双方会得到各自模型的一部分,拼在一起其实即是完整的模型,效果等同于将数据放在一起去做模型训练。通过这种方式可实现原始数据不出库的情况下的模型训练。
这需要多个参与方协同,在整个计算过程中是没有起到调度作用的中心节点。且以密文计算为基础,涉及多种密码学技术的使用,使得其计算量是远超明文计算。在实际落地的实践中,联邦学习和隐私计算同传统机器学习一样会面临大数据量的挑战,在这种情境下我们需要考虑如何横向扩大计算能力,来适应大数据量的问题。
大数据的工程技术在过去 20 年发展得非常快,但在隐私计算领域则不然。我在初入行业的时候发现,隐私计算领域的工程实现仿佛又回到了 20 年前,几乎看不到如高可用、分布式计算等我在之前的工作中常常接触的技术。行业的工程技术实现基本上还是停留在单体架构的阶段,计算在一个计算节点内完成。
在海量数据积累的今天,数牍意识到大数据量处理是隐私计算工程落地过程中不可避免的趋势,我们于是开始考虑在隐私计算领域里去做分布式计算,同时也曾想过能否基于已有的分布式计算的框架,比如 Spark,进行二次开发来适应隐私计算的场景。
最终还是决定“重造车轮”的原因如下:首先是成本,所有的开源的大数据框架都有一个最基本的前提,那就是有一个可进行中心调度的节点,所有的计算发生在一个数据中心内。但在隐私计算是异构的多参与方协作的过程,计算是没有中心的,如果修改某个现存的大数据的分布式计算框架,就意味着我们需要从推翻这个假设,从非常底层去对进行改造;甚至于,开源框架中一些基于这个假设的所进行得非常精巧高效的设计,反而会成为我们二次开发的改造难点。所以,重新造轮子,反而是针对我们的特殊场景的更合理的选择。
另外由于涉及大量的密文计算,隐私计算的计算量远远超过明文计算,现有的开源框架多数用 Java 或 Scala 等基于 JVM 语言编写,计算性能和内存管理稍差,不如 Rust 可以让我们在保证内存安全的前提下写出高效的代码。同时我们还要用到用 C 语言编写的密码学的库,相比于 JAVA,Rust 在 C 语言的 binding 上也更有优势。在上述情况下用 Rust 语言相对而言会更方便。
Q:我们爱 Rust 什么?
X:性能非常好;灵活;可将机器性能“吃干榨净”
首先 Rust 在能够较为容易的保证内存安全的前提下,有非常好的性能。我想大家应该都不止一次地听人谈起对 Rust 性能的称赞,关于这点我还有补充想法以供参考。一方面 Rust 提供了允许使用相对简单的方式,让开发者可以写出效率很好的代码:比如可以直接使用 async 关键字来实现异步函数,使用智能指针来传递地址。
另一方面,在一些特殊的场景中,Rust 也允许我们在更深入理解它的原理的基础上,将它的性能进一步发挥到极致。比如在实现异步函数时,我们也可以直接实现 Future trait,完成整个状态机的逻辑,对完整的异步执行的每一个步骤精细控制,把性能推向极致;在 C++中,为了保证内存安全,我们一般会避免直接传递裸指针,然而 Rust 的 lifetime 机制,在保证内存安全的前提下避免使用智能指针的额外消耗。同时 Rust 的内存管理的所有权机制新颖而又高效,既帮助开发者避免了如 GC 等内存管理的额外消耗,同时也能够帮助我们避免一些如冗余内存拷贝等低效代码的出现,提高我们代码开发质量的同时,显著缩短了我们在运行时 debug 的实现。
最后,和 C++相比,Rust 的语法风格统一现代,开发起来很有乐趣,并且它的性能并不亚于,甚至强于 C++。与 C++相比,Rust 的依赖管理,编译工具链等相关配套生态也非常好用的。它的宏,过程宏,trait 等语法特性也使得 Rust 的开发灵活简洁,C binding 的 FFI 也比较完善。
总结来说,相比于 C++,Rust 给我们开发者提供了一个更能兼顾性能、安全、开发效率甚至是开发乐趣的选项;以后在性能优先的开发场景中,我的首选会是 Rust 而不是 C++。
Q:Rust 的改进空间在哪里?
X:维护进展慢;语法功能有待提高
这一两年里用 Rust 比较多,有些时候确实能感觉到它存在有待提高之处。C++出现的时间很长,经过长期的迭代相对而言比较完备。Rust 一方面出现的时间短,另外一方面又是由开源社区维护的,致使很多情况的进展较慢,github 上几年没有修复的 bug 或者完成的 RFC 可以说比比皆是。再者是,Rust 的相关生态不算完善,举几个例子,比如像 gRPC,Apache Arrow 等项目虽然都有 Rust 实现,但很多是社区贡献的,相比较 C++而言,很多链路的性能均有差距,我们甚至听说有些公司,直接采用 C binding 的方式在 Rust 里直接使用 C++实现的 gRPC 库,也是从侧面反映了注意问题。
Rust 的语法还是没有 C++那么全面强大。我们感到的痛点还体现在模板方面,尤其是模板的偏特化、 Rust 有一个较为基本的模板特化的支持,但适用的场景较长,完全没有模板偏特化支持,更别提 SFINAE 了,这使得我们在一些需要较为灵活的开发场景中,需要想很多办法去实现一个在 C++里可以轻松完成的功能。
最后 Rust 的 lifetime 机制虽然是保证了内存安全,但有些时候也过于保守,编译报错的信息可读性也欠佳,需要开发者花很长时间去反复修改代码。同时现在 Rust 也缺乏一个友好的开发环境,本身的学习曲线也比较陡峭,市场上相关的人才较少,我们招聘 Rust 工程师的难度也比较大。
如果你对 Rust 或隐私计算感兴趣,欢迎加入数牍,一起探索前沿且酷的工作,用技术塑造一个新领域。
版权声明: 本文为 InfoQ 作者【数牍科技】的原创文章。
原文链接:【http://xie.infoq.cn/article/f0670cbdd14742336d3f4dd88】。文章转载请联系作者。
评论