我为一种简单的语言创建了一个interprter。它是基于AST的(更确切地说,是一个不规则的异构AST),访问者正在执行和评估节点。但是我注意到它与“真正的”解释器相比非常慢。为了测试,我运行了这段代码:
i = 3
j = 3
has = false
while i < 10000
j = 3
has = false
while j <= i / 2
if i % j == 0 then
has = true
end
j = j+2
end
if has == false then
puts i
end
i = i+2
end
在ruby和我的翻译中(只是原始地找到素数)。 Ruby完成时间不到0.63秒,我的翻译时间超过15秒。
我使用C ++和Visual Studio开发解释器,所以我使用了探查器来查看花费最多时间的方法:评估方法。 50%的执行时间是调用抽象评估方法,然后转换传递的表达式并调用正确的eval方法。像这样:
Value * eval (Exp * exp)
{
switch (exp->type)
{
case EXP_ADDITION:
eval ((AdditionExp*) exp);
break;
...
}
}
我可以将eval方法放入Exp节点本身,但我想保持节点清洁(Terence Parr在他的书中讨论了可重复使用的问题)。
同样在评估时,我总是重建Value对象,它存储已计算表达式的结果。实际上,Value是抽象的,它为不同类型派生了值类(这就是我使用指针的原因,以避免返回时的对象切片)。我认为这可能是缓慢的另一个原因。
我怎样才能使我的解释器尽可能优化?我应该从AST创建字节码然后解释字节码吗? (据我所知,它们可能会更快)
以下是有助于理解我的问题的来源:src
注意:我还没有进行任何错误处理,因此非法语句或错误只会冻结程序。 (也很抱歉愚蠢的“错误消息”:))
语法非常简单,当前执行的文件位于OTZ1core / testfiles / test.txt(这是主要查找器)。
我感谢我能得到的任何帮助,我是编译器和口译员的初学者。
答案 0 :(得分:2)
加速的一种可能性是使用功能表而不是具有动态重新输入的开关。您对typed- eval 的调用至少会经历一个,也可能是几个间接层。如果您区分键入的函数而不是按名称并给它们相同的签名,那么指向各种函数的指针可以打包到一个数组中并由类型成员索引。
value (*evaltab[])(Exp *) = { // the order of functions must match
Exp_Add, // the order type values
//...
};
然后整个开关变为:
evaltab[exp->type](exp);
1个间接,1个函数调用。快。