探索解析 API:递归的成本

主要观点:2024 年 11 月 29 日,介绍了关于解析的系列文章,对比了不同解析方式的性能。递归下降解析器性能最差,而通过迭代器(“iter”变体)和推送(“push”变体)方式实现的解析器性能更好,且更复杂的设置却能带来更好性能,消除递归后的解析器性能也有所提升。同时讨论了递归解析的其他考虑因素,如栈溢出问题,在不同场景下处理方式不同,不能一概而论地避免递归解析。
关键信息

  • 11 月 22 日的文章看了几种解析简单类 JSON 语言的方式,11 月 28 日文章实现了几个词法分析器并结合之前的解析器看性能。
  • 不同解析方式的性能数据:递归下降 127 Mb/s,tokenize_iter + events_iter 到 AST 138 Mb/s,tokenize_push + events_push 到 AST 151 Mb/s。
  • 递归下降解析器性能差的原因猜测是递归使控制流更复杂,运行时需根据调用约定移动数据导致内存读写。
  • 测试中出现栈溢出问题,在不同场景下对栈溢出的处理方式不同,如在 Haskell、Dart 中可作为异常处理,在 Rust 中可在线程边界处理。
    重要细节
  • “iter”变体是每次返回一个解析或词法分析事件的迭代器,“push”变体有处理事件和构建 AST 的监听器。
  • 递归下降解析器是一个递归调用自身处理嵌套对象的函数,消除递归后性能提升。
  • 代码可在 github.com/osa1/how-to-parse-3 获取,测试时可在 release 模式下避免栈溢出,还可生成 100M 输入并单独运行解析器进行性能分析。
阅读 4
0 条评论