15

我正在开发一个编译器,我想提高它的性能。我发现大约 50% 的时间用于解析源文件。由于源文件很小,之后我做了很多转换,在我看来它是完美的。

我的解析器是一个带有词法分析器(带有 lexer::pos_iterator)的 Boost Spirit 解析器,我有一个中等大小的语法。我正在将源解析为 AST。

我的问题是我不知道在解析过程中花费最多时间的是什么:AST 节点、词法分析器、解析器规则或内存的副本。

我不认为这是 I/O 问题,因为我正在使用 SSD,并且我在开始时完全读取文件,然后仅使用内存版本。

我尝试使用分析器,但需要时间的方法是来自 Boost 的一些方法,它们的名称有数百个字符长,我不知道它们到底做了什么......

那么,有没有一种首选的方法来对 Boost Spirit Parser 及其语法进行基准测试?或者是否有一些规则可以用来验证某些特定点的效率?

谢谢

感兴趣的人的来源:

4

1 回答 1

13

我已经快速扫描了一些东西。

我的分析器很快告诉我,构建语法和(尤其是)词法分析器对象需要相当多的资源。

实际上,只需更改 SpiritParser.cpp 中的一行,就可以节省 40% 的执行时间1(约 28 秒至约 17 秒):

    lexer::Lexer lexer;

进入

    static const lexer::Lexer lexer;

现在,

  • 使语法静态涉及使其无状态。我做到了这一点

    • 进入(使用)position_beginqi::_aqi::locals
    • 在适当的时候将其作为继承属性传入

      • 和语法,EDDIGrammar例如ValueGrammar

        start %= qi::eps [ qi::_a = qi::_r1 ] >> program;
        
      • 以及外部ValueGrammar使用的各个规则)。

    这有许多次优的副作用:

    • 规则调试被注释掉,因为lexer::pos_iterator_type没有默认输出流过载
    • qi::position(position_begin)表达式已被“伪造”并进行了相当复杂的替换:

      auto local_pos = qi::lazy (
              boost::phoenix::construct<qi::position>(qi::_a)
          );
      

      这似乎并不理想。(理想情况下,人们想qi::position用一个修改过的自定义解析器指令替换,该指令知道如何从 qi::locals (?) 获取 begin_position,这样就不需要懒惰地调用解析器表达式

无论如何,实施这些进一步的更改2 又减少了约 15% 的执行时间

static const lexer::Lexer lexer;
static const parser::EddiGrammar grammar(lexer); 

try {
    bool r = spirit::lex::tokenize_and_parse(
            position_begin, position_end,
            lexer, 
            grammar(boost::phoenix::cref(position_begin)),
            program);

松散的想法:

  • 您是否考虑过生成静态词法分析器(生成静态分析器
  • 您是否考虑过使用期望点来潜在地减少回溯的数量(注意:我没有测量该区域的任何内容)
  • 您是否考虑过Position::file和的替代方案Position::theLine?复制字符串似乎比必要的要重。我更喜欢存储const char *. 您还可以查看Boost Flyweight
  • qi::position您的指令中是否真的需要预先跳过?
  • (有点不严肃:你考虑过移植到Spirit X3吗?它似乎以移动语义的形式承诺了潜在的好处。)

希望这可以帮助。


[1]像这样(github)解析所有测试用例test/cases/*.eddi100 次时:

for (int i=0; i<100; i++)
    for (auto& fname : argv)
{
    eddic::ast::SourceFile program;
    std::cout << fname << ": " << std::boolalpha << parser.parse(fname, program, nullptr) << "\n";
}

定时简单

time ./test ../../test/cases/*.eddi | md5sum

作为md5sum一个健全的检查。

[2]我在这里创建了一个带有概念验证重构的拉取请求https://github.com/wichtounet/eddic/pull/52

于 2013-06-05T03:32:41.120 回答