主要内容总结:
- 优化数学表达式解析器:在 Rust 中编写数学表达式解析器并优化其速度和内存效率,以处理包含加法、减法和括号的简单数学表达式,如
4 + 5 + 2 - 1
等。 基线实现(43.1s):
- 程序读取
input.txt
文件中的数学表达式,通过tokenize
函数将输入字符串分割成标记,eval
函数处理表达式,parse_expression
和parse_primary
等函数递归地解析表达式。 - 例如解析
(1 + 2) - 3
,通过多个函数的调用和递归处理,最终得出结果0
。 - 此基线解析器虽能工作,但未优化,在 1.5GB 的测试文件上需 43.87 秒。
- 程序读取
优化步骤及效果:
- 优化 1:避免分配向量:使用
cargo flamegraph
和dhat
分析发现大部分时间花费在tokenizer
函数的字符串分配上,将tokenize
函数改为直接返回迭代器,避免分配向量,速度从 43.1s 提升至 6.45s,减少 85%。 - 优化 2:直接解析输入字节:分析火焰图发现虽避免了向量分配,但仍有字符串处理开销,改用
&[u8]
和手动扫描字节来避免临时字符串分配,速度从 6.45s 降至 3.68s,减少 43%。 - 优化 3:不使用 Peekable:发现
Peekable
导致性能开销,将代码中的Peekable
移除,直接使用普通迭代器处理令牌,速度从 3.68s 降至 3.21s,减少 13%。 - 优化 4:多线程和 SIMD:由于数学和语法规则限制,不能简单地将文件分割成 8 等份并行计算,使用 SIMD 找到最接近的有效分割点,然后并行处理每个块,速度从 3.21s 降至 2.21s,减少 31%。
- 优化 5:内存映射 I/O:分析内存使用情况发现仍在堆上分配大缓冲区,使用
mmap
避免额外的内存复制和虚假共享争用,速度从 2.21s 降至 0.98s,减少 56%。
- 优化 1:避免分配向量:使用
- 结论:通过一系列优化,将简单数学表达式解析器的运行时间从 43 秒降低至不到 1 秒,包括避免一次性创建标记列表、处理字节而不是文本、简化代码、使用多线程和 SIMD 以及使用内存映射文件等操作。可在https://github.com/RPallas92/math_parser找到完整代码。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。