Rust 编译模型之殇
发布日期:2021-05-16 16:56:14 浏览次数:22 分类:精选文章

本文共 2994 字,大约阅读时间需要 9 分钟。

Rust 编译缓慢的根由在于语言的设计

作者介绍

Brian Anderson 是 Rust 编程语言及其姊妹项目 Servo Web 浏览器的共同创始人之一。他目前在 PingCAP 担任高级数据库工程师。

感谢 Rust 中文社区翻译小组对本文翻译及审校上的贡献:

  • 翻译:张汉东、黄珏珅
  • 审校:吴聪

Rust 编译缓慢的根由在于语言的设计

人们常说,语言的设计是一个不断权衡的过程。最主要的权衡就在于运行时性能和编译时性能之间的平衡。而 Rust 团队偏向于追求更好的运行时性能,这一选择直接导致了 Rust 的编译时表现不尽如人意。

Rust 与 TiKV 的编译时冒险:第 1 集

我们基于 Rust 开发了分布式存储系统 TiKV。然而,它的编译速度慢到足以让公司里的许多人不愿使用 Rust。我最近花了一些时间,与 TiKV 团队及其社区中的其他几人一起调研了编译时间缓慢的问题。

这篇文章将探讨以下几个方面:

  • 为什么 Rust 编译那么慢?
  • Rust 的发展如何造就了编译时间的缓慢?
  • 编译时用例?
  • 我们测量过的,以及想要测量但还没有或者不知道如何测量的项目?
  • 改善编译时间的一些思路?
  • 事实上未能改善编译时间的思路?
  • TiKV 编译时间的历史演进?
  • 如何组织 Rust 项目可加速编译?
  • 最近和未来,上游将对编译时间的改进?

  • PingCAP 的阴影:TiKV 编译次数 “余额不足”

    在 PingCAP,我和我的同事用 Rust 写存储节点。选择 Rust 是因为希望 TiKV 在最大程度上能够快速且可靠地构建。我发现,这个决定很棒,团队的人对此也非常满意。

    然而,大家对编译时间的抱怨却让我有些无奈。比如,在开发模式下,完全重新构建需要花费 15 分钟,而在发布模式则需要 30 分钟。对于大型系统项目的开发者来说,这样的速度或许并不太糟糕。然而,与许多开发者从现代开发环境中期望得到的速度相比, TiKV 却显得过于缓慢。

    TiKV 是一个相当巨大的代码库,拥有 200 万行 Rust 代码。而 Rust 本身包含超过 300 万行 Rust 代码,TiDB 中的其他节点是用 Go 编写的。PingCAP 的一些 Go 开发人员对不得不等待 Rust 组件的构建表示不满,因为他们习惯于快速的构建-测试迭代。


    Rust 编译缓慢的根由在于语言的设计

    继续深入,我们需要理解为什么 Rust 编译时间如此糟糕。这并非语言设计的本意。实际上,语言设计者们总是必须在运行时性能和编译时性能之间权衡,而 Rust 团队倾向于更关注运行时性能。这种根本性的权衡使得 Rust 的编译时间陷入了一个死循环。

    尽管如此,Rust 的设计理念并不完全是单纯为了编译速度。它的核心设计目标包括以下几点:

  • 实用性(Practicality):语言应该是现实世界中可以使用的;
  • 务实性(Pragmatism):语言应符合人性的开发经验,并能方便地集成到现有系统中;
  • 内存安全性(Memory-safety):必须强化内存安全,防止段错误和其他内存访问违规;
  • 高性能(Performance):性能可以与 C++ 相媲美;
  • 高并发(Concurrency):为编写并发代码提供现代化解决方案。
  • 尽管编译速度不是 Rust 设计的核心目标,但它却成为了某种程度的附带问题。事实上,Rust 的设计历史也让它一步步深陷了糟糕的编译时性能沼泽。


    Rust 的自举: why? how?

    我并不记得自己什么时候才开始意识到,Rust 的糟糕编译时间其实是一个战略性问题。在面对未来底层编程语言竞争时,这可能是一个致命的错误。在最初的几年里,我几乎完全是对 Rust 编译器进行 Hacking。对编译时间的问题,我当时并不太关心,也不认为其他大多数同事会太在意。我印象中,大部分时间 Rust 的编译速度总是很糟糕,但我觉得自己能处理好。

    我通常会在计算机上至少保留三份存储库副本,方便在其他编译器都在构建和测试时,专注于其中一份进行修改。这一行为可能也是其他 Rust 开发者的日常。然而,这种零碎化的工作流程显然不是高效的开发方式。而每次构建工作都需要切换上下文,增加了工作负担。


    Rust 的自举: else?

    从历史上看,Rust 的自举(Self-Hosting)时间一直很糟糕。自举指的是使用 Rust 来构建它自己的编译器。Rust 的自举编译时间在过去几年里发生了怎样的变化?通过这些建议和数据,我们可以看到:

  • 俄罗斯套娃(rustboot):基于 OCaml 编写,旨在构建第二个由 Rust 实现的编译器 rustc。
  • 单态化(Monomorphization):将每个泛型实例转换为各自的机器代码,从而导致代码膨胀并增加编译时间。
  • LLVM 后端(LLVM backend):LLVM 产生良好的机器代码,但编译速度相对较慢。
  • 构建 Sevro(Servo):一个基于 Rust 构建的 Web 浏览器。

  • 次要因素

    除了上述根本性原因,还有一些语言设计上的因素可能对编译性能产生影响:

  • 借用(Borrowing)——复杂的指针分析以换取运行时安全。
  • 单态化——代码膨胀导致编译时间增长。
  • 栈展开——清理调用栈需要大量的编译时登记。
  • 构建脚本——限制了缓存的可能。
  • ——隐藏代码量惊人,限制了解析能力。
  • LLVM优化器——生成大量 LLVM IR 并进行优化,加剧了编译时间问题。
  • 分开编译器和包管理器——导致冗余的信息传输。
  • 每次编译单元的代码生成——静态链接的优化潜力未被充分挖掘。
  • 单线程编译器——利用并行处理不够高效。
  • 特质一致性——限制了代码抽象化和并行化。

  • 改善 Rust 编译时间的最新进展

    现状不是否没有改善的希望。许多工作正在努力改善 Rust 的编译时间,但仍有许多途径可以探索。我最近一两年了解的进展包括:

  • 优化编译流水线——例如,通过并行运行代码生成和下游包的类型检查。
  • 并行 Rust 编译器——并行运行编译器的分析阶段。
  • 优化 MIR——在 MIR 上执行常量传播,从而减少 LLVM 对单态函数的重复工作。
  • 生成机器码——减少 Debug 模式的编译时间。
  • 改进代码膨胀优化:例如,通过消除多个包中的单态化。
  • 代码大小分析:识别和处理代码膨胀问题。
  • 语言服务器优化:提高语言服务器的响应速度,从而缩短整体编译时间。

  • 下集预告

    多年来,Rust 将自己深深地逼进了一个死角。它糟糕的编译时表现几乎成了一种“必然性”,而不是一个“可解决的问题”。TiKV 的构建速度还能让我的管理者满意吗?答案可能取决于以下几个方面:

  • Rust 编译器的优化是否足够快。
  • 上游项目对编译流水线的支持程度。
  • 在下一集中,我们将深入探讨 Rust 语言设计的细节,这些细节会不会继续导致它编译缓慢?新一代的 Rust 开发者是否能改变这一现状?一言启стати文,朋友们!


    鸣谢

    许多人参与了本系列博客。特别感谢 Niko Matsakis、Graydon Hoare 和 Ted Mielczarek 的真知卓见,以及 Calvin Weng 的校对和编辑。


    wantonda 热门文章推荐

    [关键词:Rustück, TiKV, 编译优化]

    [展示相关文章链接]

    上一篇:TiDB Contributor 人数突破 400,有关开源理想,我们同在!
    下一篇:如何做到 10T 集群数据安全备份、1GB/s 快速恢复?

    发表评论

    最新留言

    逛到本站,mark一下
    [***.202.152.39]2025年04月30日 05时08分10秒

    关于作者

        喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
    -- 愿君每日到此一游!

    推荐文章

    C语言程序设计梁海英答案,1.5 习题 2023-01-24
    c语言编写单片机中断,C语言AVR单片机中断程序写法 2023-01-24
    #pragma region、{} 2023-01-24
    ddr2的上电顺序_S5PV210 DDR2初始化 28个步骤总结 2023-01-24
    deque stack java_「集合系列」- 初探 java 集合框架图 2023-01-24
    easyexcel 导出 代码翻译converter_【starter推荐】简单高效Excel 导出工具 2023-01-24
    echarts 如何在一条柱形显示两个数字_干货 | 如何快速制作数据地图?让你的可视化逼格再高一级!... 2023-01-24
    eclipse设置utf8编码_记住没:永远不要在 MySQL 中使用 UTF8 2023-01-24
    eclipse里source的快捷方法_Eclipse快捷键/快捷操作汇总 2023-01-24
    elasticsearch 查询_Elasticsearch地理信息存储及查询之Geo_Point 2023-01-24
    embedding层_【预估排序】Embedding+MLP: 深度学习预估排序通用框架(一) 2023-01-24
    excel中最常用的30个函数_Excel玩转数据分析常用的43个函数! 2023-01-24
    flink sql设置并行度_Flink 参数配置和常见参数调优 2023-01-24
    go 字符串替换_Go 每日一库之 quicktemplate 2023-01-24
    hex editor neo下载_口袋妖怪爆焰黑手机版下载-口袋妖怪爆焰黑手游下载v4.3.0 安卓版... 2023-01-24
    hibernate mysql 关联查询_spring-boot hibernate 双向关联查询的坑 2023-01-24
    hive 建表_sqoop的使用之导入到hive和mysql 2023-01-24
    hp工作站z8装Linux,惠普Z8G4双路最小工作站 2023-01-24
    html上传图片直接保存到数据库中,Editor上传图片路径存入数据库中怎么弄? 2023-01-24
    html游戏玩不了,WinXP网页游戏玩不了怎么办有哪些解决方法 2023-01-24