内置Prolog谓词的性能(是)/ 2

时间:2016-05-11 19:05:11

标签: prolog swi-prolog sicstus-prolog

更新:2016年6月11日

我在SICStus Prolog 4.3.2中观察到的令人困惑的性能差异与最近发布的SICStus Prolog 4.3.3 完全消失。的奖励!

我更新了下面的“运行时”表,以包含SICStus Prolog 4.3.3。亮点包括:

  • (is)/2 比以前快<10>
  • val_of/2也加速了,几乎是2倍

MEGO; - )

answering 问题“Size Procedure in Prolog Language SO-user @ThanosTintinidis 提出一个明显简单的方法 1 引入初学者来推导length/2

  

[...]另请注意,E只需要实例化,因为它将评估表达式。你可以这样写:

size([], 0).
size([_|Xs], 1+E) :- size(Xs, E).
     

如果你打电话:

?- size([_,_], E).
E = 1+(1+0).
     有趣,不是吗?您可能想要评估上一个E,即致电?- size([1,2], E), N is E。 [...]

有趣吗? 很有趣! 未来会有许多有趣的实验:

  • 左倾与右倾树

    list_sizL([], 0).                             % left-leaning
    list_sizL([_|Es], N+1) :- list_sizL(Es,N).
    
    list_sizR([], 0).                             % right-leaning
    list_sizR([_|Es], 1+N) :- list_sizR(Es,N).
    
  • 内置(is)/2 vs val_of/2

    val_of(V, E) :- ( E = E1+E2 -> val_of(V1, E1), val_of(V2, E2), V is V1+V2
                    ; number(E) -> V = E
                    ).
    

为了测量运行时间,我使用不同的Prolog处理器 2 运行go(2000000)

go(L) :- 
   length(Xs, L),
   member(B_2, [list_sizL,list_sizR]),
   call(B_2, Xs, E),
   member(P_2, [is,val_of]), 
   call_time(call(P_2,N,E), T),
   ( L = N -> writeq(B_2+P_2=T), nl ; throw(up) ).

Intel Core i7-4700MQ上,我观察了SICStus和SWI的以下运行时间:

                          | SWI    | SICStus | SICStus |
                          | 7.3.20 |   4.3.2 |   4.3.3 |
       -------------------+--------+---------+---------|
       list_sizL + (is)   | 208 ms |  650 ms |   60 ms | 3.4x
       list_sizL + val_of | 381 ms |  100 ms |   60 ms | 6.3x
       -------------------+--------+---------+---------|
       list_sizR + (is)   |  88 ms |  660 ms |   70 ms | 1.2x
       list_sizR + val_of | 346 ms |  100 ms |   60 ms | 5.7x
       -------------------+--------+---------+---------|

我对这些(可重复的)结果感到困惑......有人可以告诉我发生了什么吗?

脚注1 :为了简洁和易读,可变名称略有改动。
脚注2 :使用合适的命令行参数swipl -G2G -L2G -O运行SWI-Prolog 7.3.20。 功能

2 个答案:

答案 0 :(得分:4)

您正在比较(is)/2的两种截然不同的实现。 SWI在实际评估之前检测实例化错误和循环表达式。 SICStus没有。试试这个:

?- E=(1/0)+_, X is E.
?- E=(1/0)+E, X is E.

SWI产生实例化错误和类型错误(因为无限树),而SICStus总是计算1/0并在两种情况下产生评估错误。并且(至少在这个例子中),两者都是一致的。

评估两个树结构之间的速度差异是由于SWI中ground/1acyclic_term/1中的尾递归优化。当前算法使用堆栈并从左到右访问参数。因此,嵌套在右侧的树需要恒定的辅助空间,因此更快。

SICStus使用Deutsch-Schorr-Waite用于acyclic_term/1ground/1,因此没有明确的堆栈,因此没有TRO。至少,左右两者的速度大致相同。

答案 1 :(得分:2)

简单地说,我认为SWI-Prolog将更多的优化用于算术,而SICStus为一般控制流程提供了更好的代码生成。

也许可以通过Partial Evaluation从SWI-Prolog中获得更好的表现,这是Pereira-Shieber在Prolog and Natural-Language Analysis第6章中非常好地介绍的。

你也应该给YAP一个机会:它被报道为最快的基于WAM的Prolog。我记得Jan Wielemaker在SWI-Prolog列表中指出,大部分YAP速度是通过大规模重写获得的 - 比方说,内联。我认为它的基础(当然,实施得很好)部分评估。