我正在进行一些实验,用于比较Sicstus Prolog中的不同标记启发法。
但我一直陷入'资源错误:记忆力不足'。
我很确定我在测试代码中做错了什么。
以下代码将复制我的问题:
:- use_module(library(clpfd)).
:- use_module(library(lists)).
atest( R, C ):-
X is R * C,
length( M, X),
domain( M, 0, X),
all_distinct( M ),
statistics(walltime, [_,_SinceLast]),
labeling( [],M ),
statistics(walltime, [_,SinceLast]),
write('Labeling time: '), write(SinceLast),write('ms'),nl.
% Testcode for looping through alot of variations and run one test for each variant
t1:-
Cm = 1000,
Cr = 1000,
(
for(C,0, Cm),
param([Cm,Cr])
do
(
for(R, 0, Cr ),
param([C])
do
atest( C, R )
)
).
在我调用t1谓词后不久,我收到了“资源错误:内存不足”异常。
我想在调用atest释放资源后我应该做些什么?
另外:这是衡量标签时间的正确方法吗?有没有更好的方法呢?
答案 0 :(得分:3)
你没有做任何严格的错误,但你正试图运行
length(Xs,N), domain(Xs,0,N), all_distinct(Xs), labeling([],Xs).
对于N最多1000000.系统构造深度为N的搜索树,并且必须为每个级别存储变量的中间状态和约束系统。对于大N来说,这会占用大量内存,并且很可能已经为具有大N的单运行获得了内存溢出。
第二个问题是您在递归循环中运行基准测试,即您正在有效地创建连接
atest(0,0), ..., atest(1000,1000)
并且由于每次调用atest / 2都使用其第一个解决方案成功并保持其搜索状态,这意味着您正在尝试创建一个250500250000级别的搜索树...
最简单的改进是通过将代码更改为once(atest(C,R))
来在第一个解决方案之后删除每个搜索。进一步的改进是在故障驱动的循环
(
between(0,Cm,C),
between(0,Cr,R),
once(atest(C,R)),
fail
;
true
)
可以更快,更快地释放内存,并减少由于垃圾收集造成的测量失真。
答案 1 :(得分:1)
如果您在SICStus上测试t1
使用较小的值来对shell进行测试,则可能会给出t1
只有一个答案/解决方案的错误印象。然而,这种情况并非如此!所以toplevel隐藏了你的其他答案。这是SICStus顶级特殊行为,如果查询不包含变量,则不会显示更多答案。但总共有x!许多解决方案为您的标签,x! 对于每个测试用例,其他解决方案的时间是一些随机值。您的内存不足,因为对于每个测试用例,Prolog会保留记录以继续为每个测试用例生成下一个解决方案。
我不建议使用故障驱动的循环进行测试,而是使用以下非常相似但更安全的循环:
\+ (
between(0, Cm, C),
between(0, Cr, R),
\+ atest(C, R)
).
失败驱动循环的最大区别在于atest/2
和C
的{{1}}意外失败。在故障驱动的循环中,这将基本上不被注意,而上述构造将失败。
有些系统为此目的提供谓词R
。
如果您进行计时,最好只使用列表中的 first 元素并计算差异:
forall/2
通过这种方式,对目标的替代答案将为您提供更有意义的价值。