我已经快速扫描了一些东西。
我的分析器很快告诉我,构建语法和(尤其是)词法分析器对象需要相当多的资源。
实际上,只需更改 SpiritParser.cpp 中的一行,就可以节省 40% 的执行时间1(约 28 秒至约 17 秒):
lexer::Lexer lexer;
进入
static const lexer::Lexer lexer;
现在,
使语法静态涉及使其无状态。我做到了这一点
- 进入(使用)
position_begin
和qi::_a
qi::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/*.eddi
100 次时:
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