冻结/ 2个目标阻止变得无法访问的变量

时间:2015-06-28 00:49:52

标签: prolog prolog-coroutining

我制作了以下小程序,以确定在X无法访问时是否回收了用于freeze(X,Goal)等目标的内存:

%:- use_module(library(freeze)). % Ciao Prolog needs this

freeze_many([],[]).
freeze_many([_|Xs],[V|Vs]) :-
   freeze(V,throw(error(uninstantiation_error(V),big_freeze_test/3))),
   freeze_many(Xs,Vs).

big_freeze_test(N0,N,Zs0) :-
   (  N0 > N
   -> true
   ;  freeze_many(Zs0,Zs1),
      N1 is N0+1,
      big_freeze_test(N1,N,Zs1)
   ).

让我们运行以下查询...

?- statistics, length(Zs,1000), big_freeze_test(1,500,Zs), statistics.

...使用不同的Prolog处理器并查看内存消耗情况。 有什么区别!

(AMD64) SICStus Prolog 4.3.2 : global stack in use = 108   MB
(AMD64) B-Prolog       8.1   : stack+heap   in use = 145   MB
(i386)  Ciao Prolog    1.14.2: global stack in use =  36   MB (~72 MB w/AMD64)
(AMD64) SWI-Prolog     7.3.1 : global stack in use =   0.5 MB
(AMD64) YAProlog       6.2.2 : global stack in use =  16   MB

当使用?- length(Zs,1000), big_freeze_test(1,10000,Zs).运行更多迭代时,我做了以下观察:

  • Ciao Prolog在中止前报告{ERROR: Memory allocation failed [in Realloc()]}

  • 分配的内容越来越多,直到机器冻结。

  • 执行 3.554 秒内的所有迭代。
  • 也执行所有迭代,但 36.910 秒。

任何想法为什么它适用于SWI-Prolog和YAProlog,但不适用于其他想法?

考虑到运行时间,SWI-Prolog如何击败YAProlog超过一个数量级?

我的直觉倾向于"归因变量的相互作用"用"垃圾收集"。与其他Prolog处理器相比,SWI-Prolog和YAProlog有(共享?)不同的属性变量API和实现......然后,它可能会完全不同。 谢谢!

2 个答案:

答案 0 :(得分:2)

TL; DR: bug in SWI 不再!

您正在创建500,000个冻结目标,这些目标随后无法访问。这些目标意味着什么? Prolog系统不会分析关于其语义相关性的目标(在实际执行之前)。所以我们必须假设目标可能在语义上相关。由于目标已经断开,它们可能具有的唯一语义效果是错误的,从而使下一个答案成为错误。

因此,仅考虑freeze(_,false)即可。

从语义上讲,谓词p/0q/0是等价的:

p :-
   false.

q :-
   freeze(_,false).
然而,在程序上,第一个目标失败而第二个目标成功。在这种情况下,区分解决方案答案是关键。当Prolog成功时,它会产生一个答案 - 最常见的是在Prolog中称为答案替换而没有约束,其中答案替换总是包含一个或无限多个解决方案 1 。在存在约束或粗略协调的情况下,答案现在可能包含冻结的目标或约束,必须考虑这些目标或约束才能理解实际描述的解决方案。

在上述情况下,解决方案的数量。当系统现在垃圾收集那些冻结的目标时,它实际上改变了程序的含义。

在SICStus中,显示如下:

| ?- q.
prolog:freeze(_A,user:false) ? ;
no

在SWI和YAP中,默认情况下不会显示这些目标,因此可能存在错误,因为这个错误尚未被发现。

PS:过去,各种系统之间存在a comparison关于GC和约束的问题,当时SICStus是唯一通过所有测试的系统。与此同时,一些系统得到了改进。

我首先查看了SICStus数字:每次冻结216个字节!这是27个单词,其中13个代表目标。所以只需14个单词即可冻结。还不错。

PPS:冻结目标是throw/2,应该是throw/1

精美印刷 1:一些示例:答案替换X = 1只包含一个解决方案,而X = [_A]包含无限多个解决方案,例如X = [a]以及许多等等。所有这些gets much more complex都在约束的背景下。

答案 1 :(得分:-1)

在某些设置中,收回冻结的目标无效。例如,在CC中,这是不允许的,因为一个程序至少有3个结果:

有一篇Evan Tick的论文解释了CC:

  

从初始用户开始,程序成功终止   在经过一些还原步骤后,查询(原子的并集),否   目标有待执行,也不会被暂停。或者,   如果仅保留暂停的目标,则程序死锁。第三个结果是   程序失败,将在下面更正式地定义。

并发逻辑编程语言的发展
埃文·迪克-1995
https://core.ac.uk/download/pdf/82787481.pdf

这是一个说明问题的示例。 Frozen / 2比CLP(FD)更原始。因此CLP(FD)有时可以执行以下操作:

npm install redux-devtools-extension

但是现在,如果我对冻结执行相同的操作,则会丢失所有信息,因为SWI-Prolog收回了冻结的目标:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.21)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- [user].
test(A,B) :- A #< X, X #< B.

?- test(1,2).
false

?- test(1,3).
true

也许需要对垃圾收集行为进行更精细的控制。

编辑1:建议在SWI-Prolog中进行更好的控制是使用call_residue_vars / 2包装。我不知道这是否是最聪明的解决方案,但至少它是一个解决方案:

?- [user].
test2(A,B) :- freeze(X,(integer(X),A<X)), freeze(X,(integer(X),X<B)).

?- test2(1,2).
true.

?- test2(1,3).
true.