“原文[1]
toml_edit
是一个保留格式(在修改用户的Cargo.toml
时,能保留格式)的TOML
crate,允许用户修改.toml
文件。
优化之前
toml_edit 8.7us 271us
toml_edit::easy 20.7us 634us
优化之后
toml_edit 4.0us 149us
toml_edit::easy 5.0us 179us
该作者是 cargo-edit
的核心贡献者,现在正致力于将 cargo-add
合并到 cargo
的工作中,其中对 toml
的修改要用到 toml_edit
这个库,他们已经把相关的工作都做完了,只剩下最后都性能优化。这篇文章就是 作者对 toml_edit
性能优化的记录。
1. 要确定你的优化目标。
作者之前并不确定可以从 toml_edit
中可以挤出多少性能,但是Alex Crichton帮助他确定了这个目标,他特别指出Cargo的解析器,是一个影响用户的瓶颈,这个结果是在toml-rs
分析时展示出来的。因此,toml_edit 至少应该和 toml_rs
有同样的速度,他们还想进一步优化toml_rs
。
2. 依靠性能之书[2] 来帮助开始profile,选择了callgrind和kcachegrind作为可视化工具。
3. 使用 kstring[3] ,来优化字符串key。
默认情况下,kstring :
4. 要注意第三方解析便利性背后的成本
解析器组合器,如nom或combined,使语法转换为代码变得容易,但也很容易隐藏大量的成本:
他优化解析器的 https://github.com/ordian/toml_edit/pull/209
5. 将字符和字符串的操作换成按字节操作,并且根据情况在某些安全的情况下,使用 uncheck 的方法来避免 utf-8
校验。 PR在这里[4]
6. 良好错误处理背后的代价。
toml_edit 之前的解析器用的是 combine,它的特点是错误处理非常细致,将检查每个选择并合并错误。它优先考虑在字符串中最早出现的错误,并合并发生在相同点的错误。这样做的问题是,即使没有面向用户的错误发生,错误处理的逻辑也会让你付出代价。
作者要优化他还有很多选择,比如放弃 combine
,使用nom
或者手写parse
(性能优化效果将最大),但是他选择继续使用 combine
,但是用 dispatch!
(可以迅速实施改变)来代替map
。这样的选择算是小步迈进。PR[5]
7. serde的隐藏成本
serde对toml文件中每个字符串进行解析,看它是否可能是一个日期。没有Datetime的文件必须为它的存在付出代价。所以作者将使用一个专门的结构体来优化这种情况。PR[6]
默认情况下,serde检查每个未标记的枚举的变体,看它是否有效。作者使用 手动实现 serde::Deserialize
trait 来优化这种情况,而避免 derive
自动实现。PR[7]
有哪些优化是失败的呢?
不是所有的修复都是成功的。不幸的是,失败的尝试通常不会被很好地记录下来,因为它们可能不会被记录到PR中。
这里作者凭记忆罗列了一些失败的尝试:
最后,发现了这句话:感谢 Futurewei 对这项工作的赞助
[1]
https://epage.github.io/blog/2021/09/optimizing-toml-edit/: https://epage.github.io/blog/2021/09/optimizing-toml-edit/
[2]
性能之书: https://nnethercote.github.io/perf-book/profiling.html
[3]
kstring: https://github.com/cobalt-org/kstring
[4]
这里: https://github.com/ordian/toml_edit/pull/219/
[5]
PR: https://github.com/ordian/toml_edit/pull/222
[6]
PR: https://github.com/ordian/toml_edit/pull/226
[7]
PR: https://github.com/ordian/toml_edit/pull/227