Python 调用 Rust 挺香的,向没用过的朋友们安利一下

2021-03-01 12:12:35 +08:00
 LeeReamond

简单说一下吧,python 语言效率的问题也是老生常谈了,我也是 v2 上 py 社区的老面孔了

rust 这门语言我没接触过,我对他的印象一直是那种想要干掉老语言的新语言,但是生态不行所以没戏。印象中 rust 是对标 c/c++,scala 对标 java,julia 对标 python,再加一个专门写网络服务的 go,这似乎就是新时代的样貌了。但是其实这四门语言除了 go 以外都几乎没接触过。

直到前几天跟朋友聊天,对方偶然说起 python 调用 rust 可以是一种解决方案。我正好也面临计算密集任务的困扰,就去搜了一下,一搜,发现据说很香,于是试用了一下,发现真的很香,一直香到爆炸了,我就来 v2 发帖了。

=================================

先说一下直观效果,团队现在在做数据类服务,有很多密集计算型任务,并且以后可能越加越多。刚开始写的时候肯定是 python 实现算法,但是随着数据量增多,算法变复杂,处理时间成几何倍数增加,py 已经顶不住了。所以我们之前是把一些最集中的部分先用 c++抽出来写成插件,因为我和另外一个朋友都能写点简单的 c++,这么暂时先凑合着,原计划是要用 cython 把算法部分完全重写的。

前天听说 rust 能用,当天花了几个小时过了一遍 rust 的语法,昨天把 c++的东西用 rust 完全重写了一遍,大概一千多行,今天已经上测试服务器了。并且简单跑了一下分,rust 嵌入 python 表现非常优秀。

我们重复执行同一个计算任务,原任务用 python 实现大概耗时在一分多钟,切换到 c++之后大概耗时在 3100 毫秒,今天改写成 rust 之后,在实现基本上完全相同的情况下,单次执行时间缩短到了 2300 毫秒左右,令人很惊喜。

而之后多线程压力测试中,1600 次任务,c++版本耗时 720 秒,rust 版本耗时 440 秒。这个差距相对于单核进一步拉大,似乎数据中转和线程调度上,相对于 cython 还有进一步的优势。

=================================

所以我在这里向所有在 python 任务中遇到 cpu 密集型任务瓶颈的朋友们推荐 rust 这门语言和它配套的 python 嵌入方案。目前看来几天的使用里,它可能带来的问题并不多。我们测试中遇到的为数不多的问题是似乎底层调用机制与 cython 的嵌入不同。单纯测试空函数返回时间,如果使用 cython 编译 python 函数,是可以加速函数返回的,但使用 rust 嵌入方案的话却会让返回时间减慢一倍左右。我个人也不是很了解解释器调用的实现细节,这个暂且不谈。不过只要不做特别细粒度的嵌入,这其实也不会造成什么困扰。

而与问题相对的,好处就很多了。

rust 的好处其一是它的性能强,与原生 C 比肩,并且显然在实际生产中比 c 更快。因为一般用 c++也就是用 stl 中的容器和算法搞一搞,我个人比较菜,不太有水平自己实现。在这种条件下,rust 的内置实现的几种数据结构和算法,是比 c++stl 更快的,起到了实际上性能领先的作用。

其二是 rust 的学习曲线非常平滑,本身通过限制语法自由度,在设计上屏蔽了 c++中一些让人非常头秃的一些特性(野指针之类的),加上有比较完善的内建通用数据结构,导致如果你只是单纯的实现算法的话,向我们一样简单学习几小时就直接可以工作了。并且同样由于编译器要求严格,你写出的代码只要能过编译,一般质量都还可以。

第三是 rust 的工程化很香。之前用 cython 方案,其实整个目录结构工程化不是很好做,因为 cython 这门介于 c 和 py 之间的语言,说不好听点是四不像,很多似是而非的坑需要自己踩,通常是 c 的方法也不行,py 的方法也不行,只能按照 cython 独有的规矩。这点切换到 rust 之后就不构成烦恼,因为原生模块化方案还不错,嵌入 py 时可以非常接近 py 工程目录的逻辑结构,整个代码解耦非常舒服。

第四是 rust 这门语言描述能力比较强,毕竟是一门十年左右的新语言,在编程语言大家庭中算孙子辈的了。像我们平常使用 python 的原因之一可能就是因为 py 的描述能力强大,相比之下如果用纯 c 语言写东西的话就会感觉在描述能力上束手束脚。而切换到 rust 则不会有明显的不适感,虽然相对于动态的 python 确实受到了诸多限制,但是本身语言也吸收了这么多年优秀语言的优秀特性,也加入了很多语法糖,比如下述代码实现的逻辑

fn main() {
    let list = vec![1 , 2 , 3 , 4 , 5];
    for i in 0..list.len() {
    	println!("num: {}" , list[i])
    }
}

即使你从未学习过 rust 的代码,你也会发现基本上能看懂,并且这种语言在基础使用场景的描述能力并不弱,已经十分接近自然语言逻辑。

======================================

总之胡言乱语写了这么多,与大家分享,已经很熟悉这套工具链的朋友请勿喷,我相信还有很多和我一样不了解的朋友,欢迎探讨,我们共同进步。

3159 次点击
所在节点    问与答
14 条回复
shoaly
2021-03-01 12:25:31 +08:00
越来越多的场景 会是多语言互相 协同, 利用各自优势吧...
shoaly
2021-03-01 12:45:49 +08:00
对了 ,老哥有没有比较过 rust 和 go 的时间
jokaye
2021-03-01 12:58:14 +08:00
for i in 0..list.len() —— 哎这语法糖一言难尽
LeeReamond
2021-03-01 13:14:54 +08:00
@shoaly 我不会用 python 调 go,没研究过。不过单纯语言性能的话你可以在网上搜到其他的跑分。一般来说同样逻辑 rust 肯定比 go 快,可能快几到几十百分点这样,没有数量级差距。因为 go 有 gc 而 rust 没有,但是 rust 香就香在它没有 gc 一样做到了你开发时候不需要自己考虑回收的问题
LeeReamond
2021-03-01 13:17:42 +08:00
@jokaye 挺好的啊,不知道有啥一言难尽的,我写了一千行 rust 以后感觉挺香的。毕竟 for 太常用了,像 js 那种进入 es6 以后 for 的语法大幅简化,但还是要比这种写法多写不少字。rust 这个语法的优化接近 python 了,你甚至不需要考虑 i 从哪来的问题,写码的时候能体会到一种显著的对心智负担的降低
itskingname
2021-03-01 13:39:54 +08:00
@shoaly Python 调用 Golang 可以看这篇文章: https://mp.weixin.qq.com/s/H1Fe7BXdKIeEzIxjLxlYBg
Kilerd
2021-03-01 15:33:34 +08:00
要不是看到这 for i in 0..list.len(), 我差点就以为你懂 rust 了。
liprais
2021-03-01 15:35:46 +08:00
我猜你的 python 算法肯定是 numpy 和原生类型混用的
wzb0909
2021-03-01 15:50:58 +08:00
Jirajine
2021-03-01 16:05:51 +08:00
rust 对标 cpp,提供零运行时开销的、尽可能多的抽象和各种特性。
julia 对标 c,保持简单、手动内存管理、无宏、无隐式的调用和分配。
0x0208v0
2021-03-01 20:25:57 +08:00
话说 python 怎么和 rust 通信的呀,可以发个链接学习学习吗
hsfzxjy
2021-03-01 21:33:41 +08:00
楼主是用 pyo3 吗
LeeReamond
2021-03-02 03:41:15 +08:00
@hsfzxjy
@Jirajine pyo3 很好,gh 直接用就是了。rust 也有 c 编译的方式在 py 里用 ctypes 调,但是这类通信最难解决的是类型转换问题,所以还是有转换的第三方香
runstone
49 天前
楼主用的 pyo3 去结合的么?还是别的什么工具。这里面需要注意的是 python 的整数和 float 都是无穷大的,对应 rust 没有相应的类型啊,要怎么搞?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/757216

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX