Article View: comp.lang.prolog
Article #3013theorem prover of R. Overbeek
From: Bart Demoen
Date: Tue, 23 Feb 1988 08:46
Date: Tue, 23 Feb 1988 08:46
160 lines
6828 bytes
6828 bytes
I have been busy with the theorem prover of R. Overbeek, which was used
as a benchmarking program and a challenge to the prolog community. I want
to report here on my experiences with changing the program, introducing
destructive assignment and finding a reason why his prolog program is 20
times slower than his C program for this problem.
The unchanged version of the program took 77 sec in BIMprolog, on a SUN3/50
with the boys example (it is slow, I know, the main reason is that the bit
operations on integers are not yet in assembler, they are interpreted now;
and if you try to make time measurements, you will notice how much time the
program spends in get_next_literal and get_literals)
The first change I made to the program, was to replace the definition of
num_lits/3:
num_lits(_p,_n,_num) :- c(_p,0,_m) , c(_n,_m,_num) .
c(0,_i,_i) :- ! .
c(_n,_i,_j) :- _k is _n / 2 ,
_i1 is _i + _n - 2 * _k ,
c(_k,_i1,_j) .
This reduced the runtime to 70 seconds !
By giving another prolog definition to get_next_literal and get_literals, I
gained another 22 seconds ! (I am a bit ashamed about this prolog definition,
so I will not distribute it over the net, but you can obtain it from me
privatly)
Some small changes involved:
the test for tautology before the test for forward_subsumed
(that's how it is in C !)
form_resolvent/7 is symmetric in the first 2 argument pairs (at least
that's the way it is used) so there is no need to have 2 definitions
of form_resolvent/8; accordingly the definition of form_resolvent/7
becomes
form_resolvent(Par1Pos,Par1Neg,Par2Pos,Par2Neg,
ClashLit,ResPosWord,ResNegWord) :-
Mask is \(1 << (ClashLit - 1)),
ResPosWord is ((Par1Pos \/ Par2Pos) /\ Mask ),
ResNegWord is ((Par2Neg \/ Par1Neg) /\ Mask).
(it looks so in C !)
This gained only 1 second.
Then, I replaced all if-then-else constructions by their equivalent with
disjunction and cut: for instance:
delete_from_database_clauses([Id|Rest],L,Db,NewDb) :-
( member(Id,L) ->
delete_from_database_clauses(Rest,L,Db,NewDb)
; /* otherwise -> */
delete_from_database(Db,Id,Db1),
delete_from_database_clauses(Rest,[Id|L],Db1,NewDb)
).
became:
delete_from_database_clauses([Id|Rest],L,Db,NewDb) :-
( member(Id,L) , ! ,
delete_from_database_clauses(Rest,L,Db,NewDb)
; /* otherwise -> */
delete_from_database(Db,Id,Db1),
delete_from_database_clauses(Rest,[Id|L],Db1,NewDb)
).
Gain: 4 seconds.
The program took now about 44 seconds.
Then, I introduced destructive assignment into the program, as much as possible
without changing the algorithm. So, argrep/4 and delete/3 and
form_updated_index/4 were rewritten with destructive assignment and time
dropped from 44 to 22 seconds.
Actually, there was a small problem with destructive assignment, because at
one place in the program, the old database is used after the new one has been
constructed:
subsume_and_add(Db,ResPos,ResNeg,Parents,NewDb,DelList,NewEnd) :-
( (forward_subsumed(Db,ResPos,ResNeg) ; tautology(ResPos,ResNeg) ) ->
NewDb = Db,
NewEnd = DelList
; /* otherwise -> */
add_to_sos(Db,clause(Parents,ResPos,ResNeg),NewDb,NewId),
write_added_mesg(NewId,clause(Parents,ResPos,ResNeg)),
back_subsumption(Db,NewId,ResPos,ResNeg,DelList,NewEnd)
). ^^
||
In the call to back_subsumption, the first argument can be NewDb, but then in
the second definition of backsub/6, after the call to subsumes/4, an extra test
Id =\= SubId
must be added. (the C code also shows this test)
For destructive assignment, I implemented (in the BIMprolog kernel of course) a
predicate
replaca/3 arg1 : a compound term
arg2 : an integer between 1 and the arity of arg1
arg3 : anything
effect : in arg1, the arg2'th argument is replaced by arg3
and then changed the program accordingly; for instance: the new definiton of
argrep/4:
argrep(N,Old,Value,Old) :- replaca(Old,N,Value) .
For a destructive delete/2, I had to do something similar.
It is clear that destructive assignment improved the speed, but still the prolog
program is slower by a factor at least 10 than the C version.
With destructive assignment, the program needed 2.5 times less heap.
Not only the lack of destructive assignment is the performance killer in this
program: let's look at a typical sequence in the prolog program and compare it
with the C version:
backsub(...)
:-
( subsumes(SubsumerPos,SubsumerNeg,Pos,Neg) , Id =\= SubId , ! ,
write_subsumed_mesg(Id,SubId),
add_element(DelList,Id,EndList)
;
EndList = DelList
),
backsub(...).
for (pt = lst->next_avl - 1; pt >= lst->first; pt--)
{
if ((formula_id != (*pt)->id) && Subsumes(cl,(*pt)))
{
printf("clause %d subsumes %d \n", formula_id,(*pt)->id);
add_to_deleted_list((*pt)->id);
}
}
Most prologs recognise tailrecursion and can compile it to iteration, all right.
But, instead of C's if-then-else, prolog creates a choicepoint, makes the test
subsumes/4 (which is an out-of-line call while Subsumes in C is a macro) and
either calls a general procedure to fail, or continues and executes a cut: the
prolog overhead is at least a factor of 50 compared to C ! It is almost a
surprise that prolog is only 20 times slower !
Moreover, the datastructure used in the C and prolog program for sets, (a bit
string, i.e. an integer) is very much in favour of C: in a 'typed' prolog were
dereferencing would not be needed, arithmetic could approach the speed of
C arithmetic, but not in untyped prolog.
Since prolog compiler technology is still immature, the introduction of
destructive assignment improved the speed of the program with only a factor
of 2 (from 44 to 22 seconds) which is not much; but perhaps, in a couple of
years, when we will have implemented all beautiful ideas floating around right
now, the improvement will be from 24 to 2 seconds, making destructive assignment
well worth the effort !
Technical Details
Message-ID:
<1150@kulcs.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!mnetor!uunet!mcvax!prlb2!kulcs!bimbartShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!mnetor!uunet!mcvax!prlb2!kulcs!bimbart From: bimbart@kulcs.uucp (Bart Demoen) Newsgroups: comp.lang.prolog Subject: theorem prover of R. Overbeek Message-ID: <1150@kulcs.UUCP> Date: 23 Feb 88 08:46:23 GMT Reply-To: bimbart@kulcs.UUCP (Bart Demoen) Organization: Katholieke Universiteit Leuven, Dept. Computer Science Lines: 160