1 % (c) 2014-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(runtime_profiler,[reset_runtime_profiler/0,
6 profile_failure_driven_loop/1, profile_failure_driven_loop/2,
7 profile_single_call/2, profile_single_call/3,
8 register_profiler_runtime/4, % manually register runtimes
9 tcltk_get_profile_info/1,
10 tcltk_get_operations_profile_info/1, tcltk_get_constants_profile_info/1,
11 tcltk_dot_constants_profile_info/1,
12 runtime_profile_available/0,
13 print_runtime_profile/0, % called e.g. by -prob_profile
14
15 start_profile/2, opt_stop_profile/5,
16 stop_constant_profile/5,
17 constants_profiling_on/0,
18
19 runtime_observe_available/0,
20 observe_runtime_wf/4,
21
22 profile_recursion/3
23 ]).
24
25 % TO DO: optionally load more precise microsecond timer
26
27 :- use_module(module_information,[module_info/2]).
28 :- module_info(group,profiling).
29 :- module_info(description,'This module provides a simple runtime profiler.').
30
31 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
32
33
34 :- use_module(probsrc(kernel_waitflags),[add_message_wf/5]).
35 :- use_module(preferences,[preference/2,get_preference/2]).
36 :- use_module(probsrc(tools_strings),[ajoin/2]).
37 runtime_observe_available :- preference(performance_monitoring_on,true).
38
39 observe_runtime_wf(Start,Msg,Span,WF) :-
40 preference(performance_monitoring_on,true),
41 !,
42 get_preference(performance_monitoring_expansion_limit,Limit),
43 statistics(runtime,[End,_]), TotRuntime is End-Start,
44 (TotRuntime>=Limit
45 -> ajoin([TotRuntime,' ms'],Time),
46 add_message_wf(delay,Msg,Time,Span,WF)
47 ; true).
48 observe_runtime_wf(_StartMs,_Msg,_,_).
49
50 :- dynamic recursion_level/1.
51 % TO DO: use counter
52 recursion_level(0).
53 inc_recur(X) :- retract(recursion_level(X)), X1 is X+1,
54 assertz(recursion_level(X1)).
55 dec_recur(X) :- retract(recursion_level(X1)), X is X1-1,
56 assertz(recursion_level(X)).
57 reset_recur(X) :- retract(recursion_level(_)), assertz(recursion_level(X)).
58 :- meta_predicate profile_recursion(0,+,+).
59 profile_recursion(Call,Name,Arg) :-
60 inc_recur(X),
61 statistics(walltime,[Start,_]),
62 indent(X),format('>> ~w,~w,~w ms (ENTER),~w~n',[X1,Name,Start,Arg]),
63 call_cleanup(Call,
64 (statistics(walltime,[End,_]), Tot is End-Start,
65 indent(X),format('<< ~w,~w,~w ms (EXIT)~n',[X1,Name,Tot]),
66 reset_recur(X)
67 )).
68 indent(0) :- !.
69 indent(N) :- N>0,!, N1 is N-1, print(' '), indent(N1).
70
71
72 tcltk_get_profile_info(List) :- tcltk_get_profile_info(_,List).
73 % can be generated from command-line using -csv prob_profile_info FILE
74 tcltk_get_operations_profile_info(List) :-
75 tcltk_get_profile_info(top_level,List).
76
77
78 :- if(environ(prob_profile, false)). %used to be \+ environ(prob_profile, true)).
79 % completely disable profiling
80 reset_runtime_profiler.
81 :- meta_predicate profile_single_call(-,-,0).
82 profile_single_call(_Label,_ID,C) :- call(C).
83 :- meta_predicate profile_single_call(-,0).
84 profile_single_call(_Label,C) :- call(C).
85 register_profiler_runtime(_,_,_,_).
86 profile_failure_driven_loop(_).
87 profile_failure_driven_loop(_,_).
88 print_runtime_profile :-
89 print('No runtime profiling information available'),nl,
90 print('Recompile ProB with -Dprob_profile=true'),nl.
91 tcltk_get_profile_info(_,list([I1,I2])) :-
92 I1='No runtime profiling information available',
93 I2='Recompile ProB with -Dprob_profile=true'.
94 tcltk_get_constants_profile_info(List) :- tcltk_get_profile_info(_,List).
95 runtime_profile_available :- fail.
96
97 start_profile(_,_).
98 stop_profile(_,_,_).
99 opt_stop_profile(_,_,_,_,_).
100 stop_constant_profile(_,_,_,_,_).
101 constants_profiling_on :- fail.
102
103 :- else.
104 runtime_profile_available :- preference(prob_profiling_on,true).
105
106 :- volatile total_runtime/8.
107 :- dynamic total_runtime/8.
108
109 reset_runtime_profiler :- % print('RESET '),nl, print_runtime_profile,
110 retractall(total_runtime(_,_,_,_,_,_,_,_)).
111
112 :- use_module(eventhandling,[register_event_listener/3]).
113 :- register_event_listener(clear_specification,reset_runtime_profiler,
114 'Reset runtime profiler.').
115
116 :- meta_predicate profile_single_call(-,-,0).
117 ?profile_single_call(_,_,Call) :- preference(prob_profiling_on,false), !, call(Call).
118 profile_single_call(Label,ID,C) :-
119 start_profile(Label,StartInfo),
120 if(call(C),stop_profile(Label,ID,StartInfo),(stop_profile(Label,ID,StartInfo),fail)).
121
122 :- meta_predicate profile_single_call(-,0).
123 profile_single_call(Label,C) :- profile_single_call(Label,unknown,C).
124
125
126 % register runtime for a failure driven loop:
127 profile_failure_driven_loop(ActionName) :- profile_failure_driven_loop(ActionName,unknown).
128
129 profile_failure_driven_loop(_,_) :- preference(prob_profiling_on,false), !.
130 profile_failure_driven_loop(ActionName,StateID) :-
131 start_profile(ActionName,Info),
132 %format('Start Action ----> ~w (~w)~n',[ActionName,Info]),
133 register_time(ActionName,StateID,Info).
134 register_time(_,_,_).
135 register_time(AN,StateID,Info) :-
136 stop_profile(AN,StateID,Info),
137 fail.
138
139 start_profile(_,P) :- preference(prob_profiling_on,false), !, P=[0,0].
140 start_profile(_Label,[RT1,WT1]) :-
141 statistics(runtime,[RT1,_Delta]),
142 statistics(walltime,[WT1,_]).
143 % inc_recur(X), indent(X), format('>> ~w, ~w, ~w ms (ENTER)~n',[X,_Label,WT1]).
144 stop_profile(_,_,_) :- preference(prob_profiling_on,false), !.
145 stop_profile(Label,StateID,[RT1,WT1]) :-
146 statistics(runtime,[RT2,_]),
147 statistics(walltime,[WT2,_]),
148 DRT is RT2-RT1, DWT is WT2-WT1,
149 %dec_recur(X),indent(X), format('<< ~w, ~w, ~w ms (EXIT)~n',[X,Label,DWT]),
150 %format('Runtime for ~w : ~w ms (~w wall)~n',[AN,DRT,DWT]),
151 register_profiler_runtime(Label,StateID,DRT,DWT).
152 % now a version with an additional prefix context and only register values with a certain threshold
153 opt_stop_profile(_,_,_,_,_) :- preference(prob_profiling_on,false), !.
154 opt_stop_profile(Category,Label,StateID,[RT1,WT1],Threshold) :-
155 statistics(walltime,[WT2,_]), DWT is WT2-WT1, DWT>=Threshold,
156 !,
157 statistics(runtime,[RT2,_]),
158 DRT is RT2-RT1,
159 register_profiler_runtime(Label,Category,StateID,DRT,DWT).
160 opt_stop_profile(_,_,_,_,_).
161
162 % register_profiler_runtime(Label,StateID,DeltaRunTime,DeltaWallTime)
163 %register_profiler_runtime(_,_,0,0) :- !. % comment out if you want to count all calls
164 register_profiler_runtime(_,_,_,_) :- preference(prob_profiling_on,false), !.
165 register_profiler_runtime(PP,ID,RTime,WTime) :-
166 register_profiler_runtime(PP,top_level,ID,RTime,WTime).
167 register_profiler_runtime(ProgramPoint,Category,StateID,RTime,WTime) :-
168 (var(ProgramPoint) -> PP2='unknown' ; PP2 = ProgramPoint),
169 (retract(total_runtime(PP2,Category,OldR,OldW,Calls,Max,MaxID,Min))
170 -> true
171 ; OldR=0,OldW=0,Max=0,MaxID=StateID,Calls=0,Min=inf
172 ),
173 ((RTime<0 ; WTime <0) -> print(negative_runtime(PP2,StateID,RTime,WTime)),nl ; true),
174 NewR is OldR+RTime, NewW is OldW+WTime, NewCalls is Calls+1,
175 (WTime > Max -> NewMax = WTime, NewMaxID=StateID ; NewMax = Max, NewMaxID=MaxID),
176 (number(Min), Min < WTime -> NewMin = Min ; NewMin = WTime),
177 assertz(total_runtime(PP2,Category,NewR,NewW,NewCalls,NewMax,NewMaxID,NewMin)).
178
179
180
181 constants_profiling_on :- preference(performance_monitoring_on,true). % TODO: maybe use other preference later
182
183 stop_constant_profile(Val,Phase,ID,IDInfo,Timer) :-
184 (get_value_info(Val,ValKind) -> IDInfo2 = [ValKind|IDInfo] ; IDInfo2=IDInfo),
185 opt_stop_profile(constant_computation(Phase,IDInfo2),ID,root,Timer,0).
186
187 get_value_info(Var,ValKind) :- var(Var),!, ValKind=variable.
188 get_value_info(closure(_,_,_),ValKind) :- !, ValKind=symbolic.
189 get_value_info(avl_set(_),ValKind) :- !, ValKind=avl_set.
190 get_value_info(rec(_),ValKind) :- !, ValKind=record.
191 get_value_info(int(_),ValKind) :- !, ValKind=integer.
192 get_value_info(string(_),ValKind) :- !, ValKind=string.
193 get_value_info(fd(_,GS),ValKind) :- !, ValKind=GS.
194 get_value_info(freeval(_ID,_,_),ValKind) :- !, ValKind=freeval.
195 get_value_info(pred_false,ValKind) :- !, ValKind=boolean.
196 get_value_info(pred_true,ValKind) :- !, ValKind=boolean.
197 get_value_info([],ValKind) :- !, ValKind=empty_set.
198 get_value_info(O,other) :- functor(O,F,N), write(other(F,N)),nl,fail.
199
200
201 translate_pp(PP,top_level,PPS) :- !, translate_term_into_atom(PP,PPS).
202 translate_pp(PP,constant_computation(Phase,IDInfo),PPS) :- !,
203 ajoin([PP,'::',Phase,'::',IDInfo],PPS). % TODO: improve rendering of IDInfo
204 translate_pp(PP,Category,PPS) :- ajoin([PP,'::',Category],PPS).
205
206 relevant_total_runtime(PP,Cat,RT,WT,Calls,MaxWT,MaxID,Min) :-
207 total_runtime(PP,Cat,RT,WT,Calls,MaxWT,MaxID,Min),
208 \+ irrelevant_profile_entry(PP,Cat,WT).
209
210 irrelevant_profile_entry(ID,constant_computation(Phase,IDInfo),0) :-
211 Phase = propagate_value, % only show propagations if relevant runtime or instantiates something other than itself
212 (member(instantiates(List),IDInfo) -> List = [ID] ; true).
213
214 :- use_module(library(lists)).
215 print_runtime_profile :-
216 format('---- ProB Runtime Profiler ----~n',[]),
217 format('---- Time spent in B operations and invariant evaluation~n',[]),
218 (get_preference(prob_profiling_on,false) ->
219 format('---- *** set preference PROFILING_INFO to TRUE to obtain more information!~n',[]) ; true),
220 print_runtime_profile(top_level),
221
222 format('---- Time spent evaluating CONSTANTS (defined by equations)~n',[]),
223 print_runtime_profile(constant_computation(_,_)).
224 print_runtime_profile(Cat) :-
225 format(' ~w : ~w ms (~w ms walltime & ~w - ~w ms walltime; #calls ~w)~n',['Operation','Total Runtime','Total','Minimum','Maximum','Number of Calls']),
226 findall(profile(RT,WT,Calls,MaxWT,PP,Cat,MaxID,Min),relevant_total_runtime(PP,Cat,RT,WT,Calls,MaxWT,MaxID,Min),List),
227 sort(List,SL),
228 reverse(SL,RSL),
229 maplist(print_runtime_profile_info,RSL),
230 findall(WT2,relevant_total_runtime(_PP1,Cat,_RT1,WT2,_Calls,_MaxWT1,_MaxID1,_),WList),
231 sumlist(WList,TotalWT),
232 findall(Calls2,relevant_total_runtime(_PP2,Cat,_RT2,WT2,Calls2,_MaxWT2,_MaxID2,_),WList2),
233 sumlist(WList2,TotalCalls),
234 format(' Total Walltime: ~w ms for #calls ~w~n',[TotalWT,TotalCalls]).
235
236 print_runtime_profile_info(profile(RT,WT,NrCalls,MaxWT,PP,Cat,unknown,Min)) :-
237 translate_pp(PP,Cat,PPS),
238 format(' ~w : ~w ms (~w ms walltime & ~w - ~w ms walltime; #calls ~w)~n',
239 [PPS,RT,WT,Min,MaxWT,NrCalls]).
240 print_runtime_profile_info(profile(RT,WT,NrCalls,MaxWT,PP,Cat,MaxID,Min)) :-
241 translate_pp(PP,Cat,PPS),
242 format(' ~w : ~w ms (~w ms walltime & ~w - ~w ms walltime (max for ~w); #calls ~w)~n',
243 [PPS,RT,WT,Min,MaxWT,MaxID,NrCalls]).
244
245 tcltk_get_profile_info(Category,
246 list([list(['Operation/Action','Total Runtime (ms)','Total Walltime (ms)',
247 '# Calls','Max. Walltime (ms)','Max. Witness ID', 'Min. Walltime (ms)'])|L])) :-
248 findall(profile(RT,WT,Calls,MaxWT,AN,Category,MaxWitnessID,Min),
249 relevant_total_runtime(AN,Category,RT,WT,Calls,MaxWT,MaxWitnessID,Min),List),
250 sort(List,SL),
251 reverse(SL,RSL),
252 maplist(get_profile_list,RSL,L).
253
254 :- use_module(probsrc(tools_meta),[translate_term_into_atom/2]).
255 get_profile_list(profile(RT,WT,Calls,MaxWT,AN,Cat,MaxID,Min),list([PPS,RT,WT,Calls,MaxWT,MaxID,Min])) :-
256 translate_pp(AN,Cat,PPS).
257
258 % can be generated from command-line using -csv prob_constants_profile_info FILE
259 tcltk_get_constants_profile_info(
260 list([list(['Constant','Kind','@desc', 'Phase', 'Computation Runtime (ms)',
261 'Computation Walltime (ms)', 'Propagation Walltime (ms)',
262 '# Calls','Other Infos', 'Valued','Det.Value','Instantiates'])|L])) :-
263 findall(ProfileTerm, relevant_cst_profile_entry(ProfileTerm),List),
264 sort(List,SL),
265 reverse(SL,RSL),
266 maplist(get_cprofile_list,RSL,L).
267
268 :- use_module(bmachine, [constant_variable_marked_as_memo/1, constant_variable_marked_as_expand/1]).
269 :- use_module(b_interpreter_components,[get_det_solution_for_constant_value_kind/2]).
270 :- use_module(b_machine_hierarchy,[abstract_constant/2, concrete_constant/2]).
271 :- use_module(translate,[translate_bvalue_kind/2]).
272 :- use_module(state_space,[try_get_unique_constants_state/1]).
273 relevant_cst_profile_entry(cprofile(RT,WT,PropWT,Calls,PP,Kind,Ann,Phase,IDInfo,InstItself,ValInfo,InstList)) :-
274 (try_get_unique_constants_state(CstState) -> true ; CstState=[]),
275 (Category = constant_computation(Phase,IDInfo),
276 relevant_total_runtime(PP,Category,RT,WT,Calls,_MaxWT,_MaxWitnessID,_Min),
277 Phase \= propagate_value,
278 (abstract_constant(PP,_) -> Kind=abstract ; concrete_constant(PP,_) -> Kind=concrete ; Kind=unknown)
279 ;
280 (constants_profiling_on -> Phase = no_equation_detected
281 ; Phase = '-'), % constants profiling on needed to keep track of all equations (except arith. at the moment)
282 RT=0, WT=0, PropWT=0, Calls=0, IDInfo = [],
283 (abstract_constant(PP,_), Kind=abstract ; concrete_constant(PP,_), Kind=concrete),
284 \+ total_runtime(PP,constant_computation(_,_),_,_,_,_,_,_) % we have no profiling info at all for PP
285 ),
286 (get_det_solution_for_constant_value_kind(PP,DetValInfo)
287 -> DetSolFound = true
288 ; DetValInfo = '-', DetSolFound = false
289 ),
290 ( (DetSolFound=false ; DetValInfo='LARGE-VALUE'),
291 member(bind(PP,CstVal),CstState), translate_bvalue_kind(CstVal,ValInfo)
292 -> SolFound=true
293 ; ValInfo=DetValInfo, SolFound=DetSolFound
294 ),
295 % now check whether there are infos about propagation phase:
296 (total_runtime(PP,constant_computation(propagate_value,PropInfo),_,PropWT,_,_,_,_)
297 -> (member(instantiates(IList),PropInfo) ->
298 (select(PP,IList,InstList)
299 -> InstItself=yes
300 ; (DetSolFound=true -> InstItself = 'delayed'
301 ; SolFound=true -> InstItself = 'non-det' ; InstItself='NO'),
302 InstList=IList)
303 ; % no instantiation list stored; performance monitoring not on
304 (SolFound=true -> InstItself='-' ; InstItself='NO'),
305 InstList = '-'
306 )
307 ; PropWT = 0,
308 (DetSolFound=true -> InstItself='-' ; InstItself='NO'),
309 InstList='-'
310 ),
311 (constant_variable_marked_as_expand(PP) -> Ann=expand
312 ; constant_variable_marked_as_memo(PP) -> Ann=memo
313 ; Ann = ''
314 ).
315
316 get_cprofile_list(cprofile(RT,WT,PropWT,Calls,PP,Kind,Ann,Phase,IDInfo,InstItself,DetVal,IList),
317 list([PP,Kind,Ann,Phase,RT,WT,PropWT,Calls,IDInfo,InstItself,DetVal,IList])).
318
319
320 :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/3]).
321 % can be generated from command-line using -dot constants_profile_graph FILE
322 % TODO: improve rendering and then enable in meta_interface
323 tcltk_dot_constants_profile_info(File) :-
324 gen_dot_graph(File,constant_profile_predicate,constant_instantiates).
325
326 constant_profile_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color) :- SubGraph=none,
327 Category = constant_computation(Phase,_Infos),
328 total_runtime(NodeID,Category,_RT,WT,_Calls,_MaxWT,_MaxWitnessID,_Min),
329 (abstract_constant(NodeID,_) -> Kind=abstract ; Kind=concrete),
330 Phase \= propagate_value,
331 Shape = box,
332 (total_runtime(NodeID,constant_computation(propagate_value,_),_,WT2,_,_,_,_)
333 -> ajoin([NodeID,'\\n ',Kind,'\\n ',Phase,'\\n',WT, ' ms','\\n + ',WT2, ' ms'],NodeDesc)
334 ; ajoin([NodeID,'\\n ',Kind,'\\n ',Phase,'\\n',WT, ' ms'],NodeDesc)
335 ),
336 Style='rounded', Color=gray90.
337
338 constant_instantiates(NodeID,Label,SuccID,Color,Style) :-
339 Color=red4, Style=solid, Label = 'inst',
340 Category = constant_computation(Phase,Infos), Phase = propagate_value,
341 total_runtime(NodeID,Category,_RT,_WT,_Calls,_MaxWT,_MaxWitnessID,_Min),
342 member(instantiates(List),Infos),
343 member(SuccID,List), SuccID \= NodeID.
344
345
346 :- endif.