The profiling tool helps to find hot spots in a program that are worth optimising. It can be used any time with any compiled Prolog code, it is not necessary to use a special compilation mode or set any flags. Note however that it is not available on Windows. When
?- profile(Goal).
is called, the profiler executes the Goal in the profiling mode, which means that every 100th of a second the execution is interrupted and the profiler records the currently executing procedure.
Consider the following n-queens code.
queen(Data, Out) :- qperm(Data, Out), safe(Out). qperm([], []). qperm([X|Y], [U|V]) :- qdelete(U, X, Y, Z), qperm(Z, V). qdelete(A, A, L, L). qdelete(X, A, [H|T], [A|R]) :- qdelete(X, H, T, R). safe([]). safe([N|L]) :- nodiag(L, N, 1), safe(L). nodiag([], _, _). nodiag([N|L], B, D) :- D = |
Issuing the following query will result in the profiler recording the currently executing goal 100 times a second.
?- profile(queen([1,2,3,4,5,6,7,8,9],Out)). goal succeeded PROFILING STATISTICS -------------------- Goal: queen([1, 2, 3, 4, 5, 6, 7, 8, 9], Out) Total user time: 0.03s Predicate Module %Time Time %Cum -------------------------------------------------------- qdelete /4 eclipse 50.0% 0.01s 50.0% nodiag /3 eclipse 50.0% 0.01s 100.0% Out = [1, 3, 6, 8, 2, 4, 9, 7, 5] Yes (0.14s cpu)
From the above result we can see how the profiler output contains four important areas of information:
profile/1
predicate itself always succeeds.
Goal:
shows the goal which was
profiled.
The problem with the results displayed above is that the sampling frequency is too low when compared to the total user time spent executing the goal. In fact in the above example the profiler was only able to take two samples before the goal terminated.
The frequency at which the profiler samples is fixed, so in order to obtain more representative results one should have an auxiliary predicate which calls the goal a number of times, and compile and profile a call to this auxiliary predicate. eg.
queen_100 :- (for(_,1,100,1) do queen([1,2,3,4,5,6,7,8,9],_Out)). |
Note that, when compiled, the above do/2
loop would be
efficiently implemented and not cause overhead that would distort the
measurement.
?- profile(queen_100). goal succeeded PROFILING STATISTICS -------------------- Goal: queen_100 Total user time: 3.19s Predicate Module %Time Time %Cum -------------------------------------------------------- nodiag /3 eclipse 52.2% 1.67s 52.2% qdelete /4 eclipse 27.4% 0.87s 79.6% qperm /2 eclipse 17.0% 0.54s 96.5% safe /1 eclipse 2.8% 0.09s 99.4% queen /2 eclipse 0.6% 0.02s 100.0% Yes (3.33s cpu)
In the above example, the profiler takes over three hundred samples
resulting in a more accurate view of where the time is being spent in
the program. In this instance we can see that more than half of the
time is spent in the nodiag/3
predicate, making it an ideal
candidate for optimisation. This is left as an exercise for the
reader.