SWIProlog使用什么发生检查优化?
引用SICStus Prolog 手册:
逻辑编程背后的常用数学理论禁止创建循环项,规定每次变量与项统一时都应进行发生检查。不幸的是,发生检查会非常昂贵,以至于使 Prolog作为一种编程语言变得不切实际。
但是,我运行了这些基准测试(Prolog 的基准测试)并发现 SWI Prolog 在打开和关闭发生检查 (OC) 之间存在相当小的差异(小于 20%):
OC 关闭::- set_prolog_flag(occurs_check, false).
在.swiplrc
(重新启动)
?- run_interleaved(10).
% 768,486,984 inferences, 91.483 CPU in 91.483 seconds (100% CPU, 8400298 Lips)
true.
?- run(1).
'Program' Time GC
================================
boyer 0.453 0.059
browse 0.395 0.000
chat_parser 0.693 0.000
crypt 0.481 0.000
fast_mu 0.628 0.000
flatten 0.584 0.000
meta_qsort 0.457 0.000
mu 0.523 0.000
nreverse 0.406 0.000
poly_10 0.512 0.000
prover 0.625 0.000
qsort 0.574 0.000
queens_8 0.473 0.000
query 0.494 0.000
reducer 0.595 0.000
sendmore 0.619 0.000
simple_analyzer 0.620 0.000
tak 0.486 0.000
zebra 0.529 0.000
average 0.534 0.003
true.
OC 开启::- set_prolog_flag(occurs_check, true).
在.swiplrc
(重新启动)
?- run_interleaved(10).
% 853,189,814 inferences, 105.545 CPU in 105.580 seconds (100% CPU, 8083669 Lips)
true.
?- run(1).
'Program' Time GC
================================
boyer 0.572 0.060
browse 0.618 0.000
chat_parser 0.753 0.000
crypt 0.480 0.000
fast_mu 0.684 0.000
flatten 0.767 0.000
meta_qsort 0.659 0.000
mu 0.607 0.000
nreverse 0.547 0.000
poly_10 0.541 0.000
prover 0.705 0.000
qsort 0.660 0.000
queens_8 0.491 0.000
query 0.492 0.000
reducer 0.867 0.000
sendmore 0.629 0.000
simple_analyzer 0.757 0.000
tak 0.550 0.000
zebra 0.663 0.000
average 0.634 0.003
true.
这些基准测试不代表实际使用情况吗?(我记得在某处读到他们被特别选择为“相当有代表性”)SWI Prolog 是否实施了一些优化技巧,SICStus 的人不知道,这使得 OC 的成本很小?如果是这样,它们是否已发布?(我从 90 年代找到了一堆关于这个的论文,但我不知道它们是否是最先进的)
回答
主要优化使局部变量的统一成为一个持续的操作。
许多抽象机器,如 PLM、ZIP、WAM、VAM,为不能作为某些结构的子项的逻辑变量(称为局部变量)提供了一种特殊情况。与这些变量的统一根本不需要任何发生检查,因此可以是常数。以这种方式,大项可以“传回”而无需额外的发生检查。
如果没有这种优化,语法处理(用于解析给定列表)会在标记数量上获得二次开销。每次交回“输入列表”时(因此,从图形上讲,每次您在语法正文中的非终结符之后跨一个逗号时),都需要扫描剩余的输入列表以查找该局部变量的出现。(它在字符数上比二次方要好,因为正则表达式大多是尾递归编码的)。
这种优化是在 2007 年的 5.6.39中引入的。令人惊讶的是,即使在像 tak 这样根本没有构建单个结构的情况下,您的测量结果也会显示开销。据我所知,SWI 5.6.39 中的发生检查统一比有理树统一(对于简单的情况)运行得稍微快一点,因为(当时)不需要额外的设置。
许多进一步的发生检查优化仍有足够的空间。但这些只会发生,如果人们确实使用此功能。至于 SWI,在过去的 13 年里发生的事情并不多。
但想一想:第一个 Prolog,称为 Prolog 0,默认支持发生检查。但是从序言 I(“马赛序言”)开始,只有借口(例如您引用的那些)。至少,该标准并没有通过仅定义NSTO执行和要求unify_with_occurs_check/2
和acyclic_term/1
. 现在,像 SWI、Tau 和 Scryer 这样的 Prolog 可以通过标志选择性地提供它。
同一方向的进一步优化是 Joachim Beer 的 NEW_UNBOUND 标签,它避免了对一些堆变量的检查,但代价是更复杂的方案。请参阅重新审视发生检查问题。JLP 5(3) 1988。和 LNCS 404。