我编写了一个带有bison的verilog解析器,并使用boost :: variant存储每个规则的每个变体的所有差异情况。我使用一个小例子,即BNF表达式来显示我的数据结构:
expression :
primary
| expression + expression
primary :
(expression)
| number
存储它的数据结构是:
typedef boost::variant<
std::shared_ptr<exp1>,
std::shared_ptr<exp2>,
> expression
typedef boost::variant<
std::shared_ptr<prim1>,
std::shared_ptr<prim2>,
> primary
class exp1 / 2和prim1 / 2用于存储表达式和primary中的两种不同情况:
class exp1 : public BaseClass {
public :
std::shared_ptr<primary> mem1;
exp1(std::shared_ptr<primary> i1):
mem1(i1)
{}
}
为简化起见,我只显示exp1,而exp2和prim1 / 2相似。 在野牛文件中,规则及其操作如下所示:
expression :
primary {
$$= std::make_shared<expression>(std::make_shared<exp1>($1));
}
这样的解决方案导致两个问题:
1编译是veeeeeeeeeeeery慢,使用g ++ 4.8.4几乎花费1分钟 2运行时间不是很快
我有一个用ocaml和ocamlyacc编写的类似解析器,它支持非常优化的变量规范,并且用1秒编译,并且运行速度与上面提到的g ++版本非常相似。
我使用boost :: variant的风格有问题吗?
我将所有变量更改为类,构造函数接受shared_ptrs:
class ComponentBase {
};
Class VariantBase{
};
class prim1;
class prim2;
class exp1;
class exp2;
class expression : public VariantBase {
expression (shared_ptr<ComponentBase> i1):
VariantBase(i1) {}
}
class primary : public VariantBase {
primary (shared_ptr<ComponentBase> i1):
VariantBase(i1) {}
}
然后编译没有任何改进。似乎yacc生成的代码是缓慢的来源。
有什么建议吗?
答案 0 :(得分:6)
更新在Boost Spirit Qi中添加了演示(因为我不精通flex / bison),请参阅下面的block-quote
如果你的AST使用共享指针,我建议运行时性能不是问题。
如果您的AST使用变体,我建议编译时性能不是问题。 (因此没有理由担心:))
shared_ptrs在概念上违背了变体。 Shared_ptrs通过生命周期管理的节点动态分配来促进运行时多态性。
变体通过自动存储持续时间促进静态多态性。
如果您对运行时多态AST节点(在AST上的转换中通常非常方便)很好,那么我建议您不要使用这些变体。相反,使它们成为同一节点层次结构的一部分。
粗略草图:
删除运行时多态(和变体头,最好)将减少编译时间。当有许多模板实例化组合要进行内联和优化时,编译时间会增长。
更新
- c++14 Spirit X3 (Swift编译,快速运行)
- c++11 Spirit Qi (编译速度慢)
- c++03 Spirit Qi (编译速度慢)
这演示了为更简单的AST删除共享指针和运行时多态性。
上面提到的模板实例化和内联的爆炸式解释了&#34;老式的&#34; Qi实现编译速度慢,可能比原始代码慢。 X3版本没有这个问题。