QCon全球软件开发大会8折优惠倒计时,购票立减¥1760!了解详情 >>> 了解详情
写点什么

我用 Rust 徒手重写了一个 Spark,并把它开源了

2019 年 11 月 08 日

我用Rust徒手重写了一个Spark,并把它开源了

本文作者 Raja Sekar 已经有三年多 Spark 的使用经验,他认为 Spark 的 DataFrame 非常优秀,可以解决大多数分析工作负载问题,但仍然有一些地方使用 RDD 会更方便。于是,他萌生出了一个使用原生语言重新实现 Spark 的想法,想看看重写后在性能和资源管理效率方面可以达到怎样的效果。最后他选择了最近很火的 Rust,重写后的 FastSpark 不仅在运行速度上比 Spark 更快,而且能够节省相当多的内存,作者接下来的目标也很简单:将其作为 Apache Spark 的替代方案。


这一切都始于我对各种分布式调度器和分布式计算框架的研究,而 Spark 就是其中的一个。因为有三年多使用 Spark 的经验,所以我对 Spark 的内部原理已经有了一定的了解。Spark 之所以取得巨大成功,不仅是因为速度和效率,还因为它提供了非常直观的 API。pandas 之所以这么流行,也是因为这个。否则的话,如果出于对性能的考虑,人们可以选择其他更好的替代方案,比如 Flink、Naiad 或者像 OpenMP 这样的 HPC 框架。


Spark 是一个通用的分布式框架,RDD 非常适合用来处理非结构化数据或复杂的任务。而现在的 Spark 都是关于 DataFrame 和 SQL,它们比 RDD 更受欢迎。DataFrame 的性能比 RDD 更好。RDD 是 Spark 生态系统的基础,那为什么 DataFrame 会获得更好的性能呢?是因为做了查询优化吗?以最简单的查询为例,你可以使用 RDD 定义出最好的数据和计算流,但 DataFrame 仍然可能胜过你定义的查询。秘密在于 Tungsten 引擎。Spark 的性能主要依赖内存。Spark 在处理典型任务时,JVM 很快就会把资源消耗光。因此,Spark 使用“sun.misc.Unsafe”来直接管理原始内存。这样造成的结果是 DataFrame API 不如 RDD 灵活。它们只能处理固定的预定义数据类型,所以你不能在数据流中自由地使用数据结构或对象。在实际当中,DataFrame 可以解决大多数分析工作负载,但仍然有一些地方使用 RDD 会更方便。


于是,我有了一个使用原生语言重新实现 Spark 的想法,看看它在性能和资源管理效率方面可以达到怎样的效果。Spark 已经经过多年的优化,所以我不指望在性能上能有巨大的差别,如果有,很可能是在内存使用方面。另外,我希望它可以像 Spark 一样通用。我决定使用 Rust 来实现,因为我也没有太多其他的选择。虽然 C++也非常适合做这个,但我更喜欢 Rust 的简洁,而且 Rust 在很多方面与 Scala 相似。如果你看过代码库里的示例代码,就会知道它与典型的 Scala Spark 代码有多么相似。我实现了一个非常基本的版本,支持部分 RDD 操作和转换。不过我还没有实现文件的读取和写入,所以暂时需要手动编写代码来读取文件。


FastSpark 项目开源链接:


https://github.com/rajasekarv/native_spark


性能基准测试使用了一个 1.2TB 的 CSV 文件。


  • 使用了 5 个 GCloud 节点(1 主 4 副);

  • 机器类型:n1-standard-8(8 个 vCPU,30GB 内存)。


这是一个简单的 reduceBy 操作,参考代码:FastSparkSpark


  • FastSpark 使用的时间为:19 分钟 35 秒

  • Apache Spark 使用的时间为:1 小时 2 分钟


这个结果十分令人惊讶。我并不指望在运行速度方面会有什么不同,我比较期待的是内存的使用情况。因为任务运行在分布式进程上,无法测量 CPU 使用时间,所以我测量了执行节点在程序运行期间的 CPU 使用情况。


FastSpark


FastSpark


Apache Spark


Apache Spark


可以看到,FastSpark 进行了大量的 I/O 操作,CPU 使用率约为 28%,而 Spark CPU 使用率始终为 100%。iotop 显示,FastSpark 在执行期间 I/O 完全饱和,而 Spark 只使用了一半多。


在执行节点上,FastSpark 峰值的内存利用率不超过 150MB,而 Spark 达到了 5-6GB,并在这个范围内波动。这种巨大的差异可能是由于 JVM 对象分配造成的,对象分配是非常昂贵的操作。我最初的实现版本比当前版本慢了两倍多。最初的实现版本使用了很多克隆和装箱,移除掉一部分之后就带来了巨大的性能提升。


同样的逻辑使用 Rust 实现比 FastSpark RDD 要快两倍多。性能分析显示,上述的 FastSpark 程序在分配和系统调用方面占用了 75%的运行时间,主要是因为 Rust 实现的版本为动态分派装箱了大量数据。


下面是在本地运行 4 种不同实现版本的结果(处理 10GB 数据)。文件是 CSV 格式,并且保存在硬盘上(是的,我的硬盘很慢),这里主要关注 user 时间。


Rust 基本版本:


real    6m05.203suser    1m02.049s sys     0m8.572s
复制代码


FastSpark 版本:


real    6m32.884suser    1m48.904s sys     0m4.489s
复制代码


Apache Spark RDD 版本:


real    10m14.646suser    14m45.452s sys     0m9.021s
复制代码


Apache Spark DataFrame版本


real    8m23.422suser    10m34.975s sys     0m8.882s
复制代码


CPU 密集型任务

Spark 的分析任务几乎总是需要消耗大量 CPU,因为我们通常会使用压缩文件,如 Parquet 或 Avro。下面是读取 Parquet 文件(从 10GB CSV 文件生成,800MB 左右)并执行相同操作的结果。


FastSpark 版本:


real    1m31.327suser    4m50.342ssys     0m1.922s
复制代码


现在变成需要消耗大量 CPU,它把所有的 CPU 时间都花在解压缩和哈希操作上。


Spark DataFrame 版本:


real    0m55.227suser    2m03.311ssys     0m2.343s
复制代码


这就是 Dataframe API 提供的优化结果。代码与之前是一样的,只是用 Parquet 代替了 CSV 格式。但需要注意的是,Spark SQL 生成的代码只选择需要的列,而 FastSpark 使用 get_row_iter 遍历所有行。


我写了一段读取文件的代码,只读取需要的列,让我们看看结果。代码请参考这里


FastSpark(只选择需要的列):


real    0m13.582suser    0m34.753ssys     0m0.556s
复制代码


这样的速度相当快了。它仍然是 IO 密集的。此外,它只使用了大约 400MB 内存,而 Spark DataFrame 使用了 2-3GB。这是我更喜欢 RDD API 的原因之一,我们可以对数据流进行更灵活的控制。虽然抽象对于大多数应用程序来说是没问题的,但有时候我们需要完成一些任务,而具有良好的性能的底层 API 更适用。


实际上,这可以让 FastSpark DataFrame 变得比 Apache Spark DataFrame 更强大、更通用。与 Spark 不一样的是,FastSpark DataFrame 可以支持任意的数据类型,还可以通过为数据类型实现自定义散列来连接具有不同数据类型的列。不过 FastSpark DataFrame 还没有开源出来,因为现在还处在试验阶段。我倾向于选择类似 pandas 那样的设计,不仅灵活,还具有很高的性能。如果有可能,它还可以与 Python 对象进行互操作,但又不同于 PySpark。


这个工作流非常简单,也在非常大型的数据集上运行过,所以我选择了它。当然,这也可能是我的个人偏好。如果可能的话,请读者自己运行代码,并向我反馈结果。Spark 经过了很多优化,特别是 shuffle,在这方面我的实现(非常简单)比 Spark 要慢得多。另外,使用 FastSpark 执行 CPU 密集型任务通常会更快。


主要目标:


  • 将其作为 Apache Spark 的替代方案。这意味着用户端 API 应该要保持一致。

  • 在与 Python 集成方面比 PySpark 做得更好。


已完成:


  • 项目处于非常初级的 POC 阶段,只支持少数的 RDD 操作和转换。

  • 分布式调度器已实现,但离投入生产还差得很远,而且非常脆弱。容错和缓存还没有完成,但应该很快就能完成。


未来规划:


  • 通用的文件读取器很快就可以完成。文件接口与 Spark 的不一样。支持 HDFS 还需要做大量的工作,但支持其他文件系统应该很简单。这个项目的主要目标之一是让它成为 Apache Spark 的完全替代品(至少支持 Python 和 R 语言等非 JVM 语言),所以我将尽量保持用户端 API 的兼容性。

  • 因为代码是试验性的,所以其中有很多硬编码的东西。支持配置和部署将是下一个优先事项。

  • shuffle 操作实现得还很简单,性能也不好,这个需要改进。


原文链接:


https://medium.com/@rajasekar3eg/fastspark-a-new-fast-native-implementation-of-spark-from-scratch-368373a29a5c


2019 年 11 月 08 日 16:4426587

评论 7 条评论

发布
用户头像
真的是徒手吗? 不拿支笔吗?
2019 年 11 月 19 日 15:12
回复
用户头像
Rust编写的程序稳定性和处理性能都很不错,使用Rust写了一个Java性能分析工具Flare Profiler,可以稳定用于压测分析Java服务性能瓶颈,欢迎一起探讨学习 Java & Rust: https://github.com/kylixs/flare-profiler
2019 年 11 月 18 日 10:22
回复
用户头像
无独有偶,我用golang徒手撸了一个Flink,与Flink保持完全兼容的SQL接口,支持各种标准窗口,EventTime, watermark, checkpoint & recovery等。
2019 年 11 月 17 日 19:18
回复
目前可以网上一键生成流式作业的可执行文件:http://creek.baidubce.com/
2019 年 11 月 17 日 19:18
回复
用户头像
rust兼具底层编程语言的灵活性和高级语言的易用性,设计新颖,开发团队也在不断提供新的库新的语法功能,以后会有很多项目用Rust编写,前几天我也刚好早起打卡读书读到这本《The Rust Programming Language》,欢迎一起探讨学习https://v.huya.com/play/233707788.html
2019 年 11 月 15 日 10:38
回复
最近看到用Swift做数据科学这种做法,觉得可以了解一下。
2020 年 01 月 28 日 23:30
回复
用户头像
还好我在学rust...
2019 年 11 月 11 日 14:12
回复
没有更多了
发现更多内容

2021年爆火的低代码开发技术,对企业而言有什么好处?

优秀

低代码

继续备考

IT蜗壳-Tango

5月日更

这可能是中文互联网第一本系统性讲解Terraform的入门教程

大可不加冰

云计算 IaC Terraform HashiCorp 不可变基础设施

并发王者课-青铜6:借花献佛-如何格式化Java内存工具JOL输出

技术八点半

Java 多线程 并发 并发王者课

helm-kubernetes的包管理器

片风

云原生 Helm 包管理工具

人生算法:重新启动的精神装置

石云升

读书笔记 思维模型 5月日更

另一种总结的方式

Nydia

学习笔记

【Flutter 专题】124 日常问题小结 (三) 自定义 Dialog 二三事

阿策小和尚

5月日更 Flutter 小菜 0 基础学习 Flutter Android 小菜鸟

Pandas 读取 Excel 文件去掉首尾的换行符

junsircoding

pandas

吾日三省 - DAY 16

Qien Z.

5月日更 半年总结

谋而后动:解读数仓计划生成中行数估算和路径生成的奥秘

华为云开发者社区

计划 数仓 GaussDB(DWS) 查询语句 估算

并发王者课-青铜5:一探究竟-如何从synchronized理解Java对象头中的锁

技术八点半

Java 多线程 并发 并发王者课

网络攻防学习笔记 Day26

穿过生命散发芬芳

5月日更 网络攻防

前端开发:npm run serve和npm run dev的区别

三掌柜

5月日更

Django 之视图篇

若尘

django 视图 Python编程 5月日更

安全专栏加餐

escray

极客时间 安全 学习笔记 5月日更 安全攻防技能30讲

Terraform中使用prevent_destroy搭配override文件防止误删资源

大可不加冰

云计算 基础设施即代码 IaC Terraform HashiCorp

用Terraform申请Letsencrypt证书

大可不加冰

云计算 证书 IaC Terraform HashiCorp

出于信仰,我去考了一个证

大可不加冰

云计算 IaC Terraform HashiCorp

亚马逊云开发者 Meetup 又双叒叕来了,您报名了吗?

亚马逊云科技 (Amazon Web Services)

开源数据库

面向WEB开发的Docker(四):启动MySQL数据库

devpoint

Docker

Dubbo 序列化

青年IT男

dubbo

☕️【Java 技术之旅】从底层分析LockSupport原理机制

李浩宇/Alex

Java JVM lock锁 5月日更 LockSupport

数字人民币与区块链的区别与联系

CECBC区块链专委会

达利欧:我持有一些比特币,美元重回1971年,现金就是垃圾

CECBC区块链专委会

如何通过别名扩展Git

李*文

bash git git扩展

要想成为牛人,推荐学习哪种编程语言?

实力程序员

教你用User Story设计BI驾驶舱

薄荷点点

数据产品经理 用户故事地图 产品需求

2021百度之星报名开启 特设“小星星”奖项鼓励少年AI人才

百度大脑

AI 百度之星 少年

AI医疗发展中的机遇与有效监管

CECBC区块链专委会

悟透前端:加深Javascript变量函数声明提升理解

devpoint

变量声明

移动应用开发的下一站

移动应用开发的下一站

我用Rust徒手重写了一个Spark,并把它开源了-InfoQ