使解释器执行得更快

时间:2015-04-14 18:18:05

标签: optimization interpreter execution

我为一种简单的语言创建了一个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(这是主要查找器)。

我感谢我能得到的任何帮助,我是编译器和口译员的初学者。

1 个答案:

答案 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个函数调用。快。