I did a few experiments with the tabling capabilities of b-prolog version 8.1 and was quite surprised by the performance I observed.
Here is the code that I used. It counts the number of Collatz steps N
required for reducing some positive integer I
down to 1
:
%:- table posInt_CollatzSteps/2. % remove comment to enable tabling
posInt_CollatzSteps(I,N) :-
( I == 1
-> N = 0 % base case
; 1 is I /\ 1
-> I0 is I*3+1, posInt_CollatzSteps(I0,N0), N is N0+1 % odd
; I0 is I>>1, posInt_CollatzSteps(I0,N0), N is N0+1 % even
).
To determine the maximum number of reduction steps required for all integers from I0
to I
:
i0_i_maxSteps0_maxSteps(I0,I,M0,M) :-
( I0 > I
-> M0 = M
; posInt_CollatzSteps(I0,N0),
I1 is I0+1,
M1 is max(M0,N0),
i0_i_maxSteps0_maxSteps(I1,I,M1,M)
).
When I ran some queries ?- time(i0_i_maxSteps0_maxSteps(1,1000000,0,MaxSteps)).
without and with tabling, I observed the following runtimes (in seconds):
By adding :- table posInt_CollatzSteps/2.
the queries got 2x faster. Still, I'm puzzled:
I wasn't expecting this! Contrast this with the runtime that I observed with xsb version 3.6.0:
What can I do? Are there any directives or flags to help me get better performance with BProlog? I use BProlog version 8.1 64-bit edition with Linux.
Was checking against Jekejeke Prolog trailed memoing. Was only going to n=100'000. For B-Prolog the following command line worked fine on windows:
bp -s 40000000
This is said to amount to a stack/heap space of 160MB. I get both tabled and non-tabled better warm runs than cold runs:
B-Prolog n=100'000 in s:
w/o tabling: 14.276, 12.229
with tabling: 0.419, 0.143, 0.127, 0.152
The memoing code for Jekejeke uses the predicate assumez/2 from the module hypo. In contrast to B-Prolog tabling, the trailed memoing will recalculate everything upon each call. You need to manually add it to your code:
Jekejeke Prolog/Minlog n=100'000 in s:
w/o memoing: 4.521, 3.893
with memoing: 0.724, 0.573, 0.585, 0.555
So there is still room for speed improvements in Jekejeke. Concerning your B-Prolog question: When the memory is too tight, this might irregularily put additional pressure on a GC, maybe resulting in your observed behaviour.
Bye
P.S.: This is the Jekejeke Prolog memoing code. You need to install Jekejeke Minlog to have the module hypo available. Best is to install the latest release:
:- thread_local posInt_CollatzStepsm/2.
mposInt_CollatzSteps(I,N) :-
( I == 1
-> N = 0 % base case
; posInt_CollatzStepsm(I,N) %%% memo check
-> true
; 1 is I /\ 1
-> I0 is I*3+1, mposInt_CollatzSteps(I0,N0), N is N0+1, % odd
assumez(posInt_CollatzStepsm(I,N)) %%% memo add
; I0 is I>>1, mposInt_CollatzSteps(I0,N0), N is N0+1, % even
assumez(posInt_CollatzStepsm(I,N)) %%% memo add
).
?- time(mi0_i_maxSteps0_maxSteps(1, 100000, 0, R)).
% Up 724 ms, GC 71 ms, Thread Cpu 640 ms (Current 06/20/19 09:43:24)
R = 350
?- time(mi0_i_maxSteps0_maxSteps(1, 100000, 0, R)).
% Up 573 ms, GC 69 ms, Thread Cpu 500 ms (Current 06/20/19 09:43:27)
R = 350