关于铁锈膨胀的想法(2019)

2021-03-30 00:30:15

我即将接受一个将增加德鲁伊的编译时间约为3x,其可执行大小几乎2倍。在这种情况下,我认为权衡是值得的(如果没有本地化,GUI工具包是严格的玩具),但膨胀让我不开心,我认为铁锈生态系统有改善的余地。

对我来说,融资的生锈主要是关于编译时和可执行的大小。编译时间是在锈病发展经验的十大坏事列表中,但在某种程度上它是在开发人员的控制下,尤其是选择是否在臃肿的板条符合依赖性。

膨胀是软件中的流行问题,但有一些事情使其成为生锈的特殊挑战:

其中一个编译时间影响体验的彩色方式是RLS等工具。

它将与人的人有所不同,但我个人会照顾很多。我对XI-Editor的希望之一是核心将是轻量级的,特别是我们可以根据UI的疑虑。但是,释放二进制文件现在是5.9m(发布构建,窗口,并且不包含语法着色,即额外的2.1M)。我在锈病生态系统上做了一堆其他事情来减少臃肿,我会在这篇文章中吹嘘一点。

当然,我考虑在德鲁伊上编译时跳跃的原因是我想要本地化,一个重要和复杂的功能。正确执行它需要相当多的逻辑围绕语言环境匹配,Unicode和自然语言处理(例如复数规则)。我不指望这个微小的箱子。

最近的一个案例我们看到了类似的权衡是观察到unicase Dep将50K增加到下拉卡马的二元尺寸。在这种情况下,Complarmarm规范要求Unicode折叠,而且没有它,它不再符合标准。我理解削减这个角落的诱惑,但我认为在那里没有规范符合规范的版本是一件坏事,特别是对世界上大多数人的母语是英语之外的大多数人。

因此,重要的是不要将精益工程混淆,缺乏重要的功能。我会说臃肿是不需要的资源消耗,超出了满足要求的必要条件。 Unicode和International化是一个特别有争议的点,因为它们实际上确实需要代码和数据来实现正确,但也因为膨胀的潜力也是如此。

我将申请更高的标准“基础”板条箱,旨在由需要功能的大多数生锈应用程序使用。在那些中膨胀是一种不使用依赖的原因,或者根据膨胀的需求和容忍来将生态系统分段为不同的解决方案。

我认为特殊风险是提供一般有用的特征的条箱,肯定会使切割的“包括”语言。其中一些(bitflags,lazy_static,cfg-if等)不是很重,并提供明显的好处,特别是使API更加人性化。对于其他人(租赁,失败),成本更高,我普遍建议不要在基础箱中使用它们。但是对于你自己的应用程序,如果你喜欢他们,肯定。我相信租赁可能是流利最昂贵的传递依赖性,因为我发现它需要27.3秒(调试,窗户;释放53.2秒)。

我担心在GFX-RS中膨胀 - 大约一分钟拍摄调试构建,大约3米(Windows,Quad示例)。出于这个原因(和稳定性和文档),我倾向于让PIET的GPU渲染器直接使用底层图形API而不是使用此抽象层。我发现了类似的模式与其他“包装器”板条箱,包括Direct2D。但这里的权衡很复杂。

[之后添加的更新Re GFX-RS:响应这篇文章,GFX-RS团队非常快速地降落了编译时间的重大改进,而Kvark也指出,我的使用Quad示例的方法无效,因为它会拉到a束实际上不需要运行GFX-R的其他依赖项。我很高兴能够注意到这一点,我很担心很大,所以将在使用GFX-R款中仔细观察未来的GPU工作。我注意到了从其他包装板箱的编译时间影响,因此普通建议仔细看看这些仍然存在。]

我还没有硬度,但我发现Rust-Objc宏产生了相当臃肿的代码,每种方法调用1.5k的顺序。这导致我考虑直接在Objective-C中重写麦斯卡斯平原绑定代码(使用C作为常见的FFI不太糟糕),而不是依赖于使用动态目标-C运行时的生锈代码。我希望在这里膨胀,以影响调用麦斯科斯乐队(和iOS)平台的相当广泛的代码,因此可以更深入地调查这是一个好主题。

我有时会听到依靠常用的箱子,因为他们的成本在分享它们的各种用户中摊销。由于各种原因,我不相信。对于一个,常见的是,无论如何,您得到了不同的版本其次,如果泛型使用大量(见下文),则可能是代码复制。

也就是说,对于像Unicode数据等的东西,它非常重要,即在二进制中可以很少有副本。最佳选择是倾斜的板条箱。

一个特别有争议的问题是proc宏。用于这些(SYN和CAITE)的支持箱需要10s来编译,并且不会直接影响可执行大小。它是对生锈语言表达性的重大推动,我们可能会在德鲁伊中使用它们,尽管已经讨论了使它们是可选的。

我个人喜欢看到的是proc宏稳定更多,然后被采用语言。

挖掘Xi-Editor,最大的单一膨胀源是SERDE,一般来说它是它将所有内容序列化为JSON消息。这是一个实验的东西,回顾我会说我最不开心的事情。似乎有效的序列化尚未解决问题。 [注意,JSON序列化在SWIFT中非常慢]

Serde如此臃肿的特定原因是它是单一的一切。有替代方案;特别是通过使用动态调度(特质物体)代替单数化的副经内较小的二进制文件和编译时。但它有其他限制,所以还没有抓住。

一般来说,过度使用多态性是膨胀的主要原因。例如,Resvg于此原因从Lyon转换为Kurbo:Razrfalcon指出,对Lyon编译时的大贡献是促进宏观,而不是多态性,而且已修复。我们没有采用Lyon / Euclid生态系统,也是为了这个原因,这是一种耻辱,因为现在有更多的碎片。在KURBO工作时,我做了表明允许除F64以外的浮点类型没有真正的好处,因此刚刚决定是坐标的类型。我对这个选择感到满意。

出于各种原因,虽然编译团队一直在取得巨大进展,但Async代码比相应的同步代码相当慢得多。尽管异步/等待是闪亮的新功能,但重要的是意识到老式的同步代码在很多情况下仍然更好。当然,如果您正在编写大型互联网服务器,则需要异步,但有很多其他案例。

我会选择Zola这一点。释放构建尺寸超过9分钟,15米。 (调试构建大约是快速但3-5倍更大的两倍)。观看编译(超过400个箱子总数!)很明显,它的网页服务(基于Actix的)占了很多,也占据了Tokio生态系统的大块。仅仅预览使用该工具构建的静态网站,它可能是矫枉过正。也就是说,对于这个特定的应用,可能膨胀并不是重要的,并且有利于使用流行的,具有一系列网络服务框架的好处。

因此,我选择不使用德国的异步,而是更简单,单线程方法,即使已经提出了异步方法。

一个箱子有一些核心功能,那么只有一些用户想要的其他东西是常见的。我认为拥有可选依赖性是一个很好的主意。例如,Xi-Rope有能力将Deltas序列化为JSON,因为我们在XI-Editor中使用它,但这是一个非常重量级依赖,只需要一个用于大字符串的高效数据结构的人。所以我们做了那个可选的。

另一种方法是将箱子分割成更精细的谷物;兰特在这里是一个特定的罪犯,因为它在构建中没有少见的是10个副押出。我们发现有很多委员会通常会使用户的生活更加困难,因为肯定版本兼容。

另一个经常出现在铁锈构建中的箱子是phf,这是完美散列的实施。这通常是一个伟大的主意,你想要在二进制文件中,但它也在使用宏版本时〜13岁的编译时间(再次引入两个单独的报价副本和SYN)。 [注释添加:SFackler指出,您可以使用PHF-Codegen生成生锈源并检查到您的repos中。]

为了优化Unicode-ranalization中的编译时,我决定使用自定义工具构建哈希表,并检查那些repo。这样,只有当数据实际上更改时(大约一年,作为Unicode Revs)而不是每一次编译时,都只能完成工作。我为这项工作感到自豪,因为它改善了大约3倍的Unicode-ranalization的编译时间,我认为这是一个重要的基本箱。

编译时间和可执行大小是性能的方面(即使通常不像运行时速度可见),并且适用性能文化。始终衡量,在适当的情况下使用货物膨胀等工具,并跟踪回归。

对于货物膨胀的一个很好的案例研究是拍手,尽管今天它仍然非常重量级(它占了大约1米的Zola调试版本,在MacOS上测量)。

还努力分析二进制规模更系统地。我赞赏这样的努力,如果他们更加明显,就会喜欢它。理想情况下,箱子将包括某种膨胀的报告以及其他元数据,尽管使用完全自动化工具有限制(例如,使用CLAP的“Hello World”示例可能是非常谦虚的,但可能有数百个选项巨大的)。

一旦接受膨胀,就很难抓住它。如果您的项目编译多分钟,人们甚至不会在编译时注意到10秒的回归。然后这些堆积起来,它变得越来越难以激励减少臃肿的工作,因为每秒在编译时获得的每秒都变得如此小的总数。

随着德鲁伊开发成真正的GUI,我将面临更多这些选择,并且编译时代和可执行尺寸都将不可避免地变得更大。但避免臃肿只是另一个应用工程技能的地方。在撰写本博客文章时,我希望提高对问题的认识,给予有用的提示,并征求社区的帮助,使防锈生态系统尽可能膨胀。

与所有工程一样,这是权衡问题。哪个对德鲁伊更重要的是,快速编译,或者在船上携带丰富的功能,例如流利的生态系统提供的丰富功能?这没有明显的答案,所以我打算大多倾听来自用户和其他开发人员的反馈。