Greetings. I was experimenting with very simple predicates making notes on conversion from non-tail recursion to tail recursion, when I stumbled upon this interesting phenomenon:
The first time you query a non-tail recursive predicate (simple recursion or stack recursion), predicate time reports a big number of Inferences and Lips. From the second time the same query is made, the number of Inferences decreases and the number of Lips increases.
On the contrary a tail-recursive predicate always reports the same numbers when queried.
It seems the compiler makes some kind of optimization and I would like to understand it.
Check the following example: a simple predicate for reversing a list in two versions, non-tail recursion and tail recursion:
%=====================
% Non-tail recursion version
%=====================
reverse01([ ], [ ]).
reverse01([X|Rest], Solution):-
reverse01(Rest, InvertedRest),
append(InvertedRest, [X], Solution).
%=====================
% Tail recursion version
%=====================
reverse02(List, Inverted):- reverse_aux(List, [ ], Inverted).
reverse_aux([ ], Acum, Acum).
reverse_aux([X|Rest], Acum, Solution):-
reverse_aux(Rest, [X|Acum], Solution).
Both versions are correct and work as expected.
Now when starting the console, I query each version two times with the same query
and get the following results:
?- time( reverse01([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], L) ).
% 26,846 inferences, 0.007 CPU in 0.007 seconds (100% CPU, **4068915 Lips**)
L = [15, 14, 13, 12, 11, 10, 9, 8, 7|...].
?- time( reverse01([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], L) ).
% 134 inferences, 0.000 CPU in 0.000 seconds (88% CPU, **4964618 Lips**)
L = [15, 14, 13, 12, 11, 10, 9, 8, 7|...].
?- time( reverse02([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], L) ).
% 15 inferences, 0.000 CPU in 0.000 seconds (84% CPU, **860042 Lips**)
L = [15, 14, 13, 12, 11, 10, 9, 8, 7|...].
?- time( reverse02([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], L) ).
% 15 inferences, 0.000 CPU in 0.000 seconds (84% CPU, **860042 Lips**)
L = [15, 14, 13, 12, 11, 10, 9, 8, 7|...].
As you can see, the non-tail recursion version performs different the first and second times it is used (26,846 inferences the first time, and 134 inferences from the second timet onwards), but the tail-recursion version behaves constant.
I then tested with various other examples and the non-tail recursive versions always behave in the same way, although with increment/decrement of different magnitude in the number of inferences.
Can you please explain this phenomenon?
Thanks in advance.