More precise clause garbage collection

I pushed some patches that refine clause garbage collection to not only keep track of the oldest goal that still accesses some predicate, but all generations up to some threshold and an interval above this threshold. This way we can remove any erased clause that is not needed for the logical update semantics of some goal. The extreme scenario is below. As the d/1 call blocked all clauses clause GC was completely ineffective (on d/1) in this scenario. In the new implementation clauses added and removed in keep playing around with d/1 are subject to clause GC as the d/1 call does not generate these anyway.

:- dynamic d/1.

go :-
    assert(d(1)),
    assert(d(2)),
    d(X),
    <keep playing around with d/1)>

If you have programs doing a lot of assert/retract with non-deterministic retract and/or access, please give this a try. The expectation is that clause GC is a bit slower, but as it runs on its own thread that shouldn’t matter much, memory use may go down (this was started due to a program that grew to 70Gb, of which 50Gb in erased but not reclaimed clauses which now stays at 1Gb for clause storage). It may also improve performance as there are fewer garbage clauses in dynamic predicates.

3 Likes