1 | % (c) 2016-2019 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(b_operation_cache, [ %project_in_state/3, | |
6 | %operation_computed/4, | |
7 | compute_operation_on_expanded_store_cache/5, | |
8 | print_op_cache/0 | |
9 | ]). | |
10 | ||
11 | ||
12 | :- use_module(module_information). | |
13 | :- module_info(group,animator). | |
14 | :- module_info(description,'This module caches B operation results on projected states.'). | |
15 | ||
16 | ||
17 | :- use_module(extension('counter/counter')). % op_cache_id counter | |
18 | ||
19 | b_operation_cache_startup :- % call once at startup to ensure all counters exist | |
20 | counter_init, | |
21 | new_counter(op_cache_id). | |
22 | ||
23 | :- use_module(eventhandling,[register_event_listener/3]). | |
24 | ||
25 | :- register_event_listener(startup_prob,b_operation_cache_startup, | |
26 | 'Initialise Statespace Counters.'). | |
27 | :- register_event_listener(clear_specification,reset_b_operation_cache, | |
28 | 'Reset Operation Cache.'). | |
29 | :- register_event_listener(change_of_animation_mode,reset_b_operation_cache, | |
30 | 'Reset Operation Cache.'). | |
31 | % TO DO: also reset if maxNrOfEnablingsPerOperation changed ? | |
32 | ||
33 | :- use_module(error_manager). | |
34 | :- use_module(bmachine,[b_get_operation_normalized_read_write_info/3]). | |
35 | ||
36 | :- dynamic operation_read_projection_cache/2. | |
37 | reset_b_operation_cache :- | |
38 | retractall(operation_read_projection_cache(_,_)), | |
39 | retractall(operation_computed(_,_,_,_)), | |
40 | retractall(operation_cached_results(_,_,_,_)), | |
41 | reset_counter(op_cache_id). | |
42 | ||
43 | % project the in-state for an operation onto the relevant variables | |
44 | project_in_state(InState,OpName,ProjectedInState ) :- | |
45 | % TO DO: statically pre-compute which operations are worthwhile for this treatment | |
46 | operation_read_projection_cache(OpName,ProjVars), | |
47 | !, | |
48 | ProjVars \= no_caching, | |
49 | (re_project_state(ProjVars,InState,Res) -> ProjectedInState=Res | |
50 | ; add_internal_error('Re-Projection failed: ',re_project_state(ProjVars,InState,_)), | |
51 | ProjectedInState = InState). | |
52 | %project_in_state(_,OpName,_ ) :- \+ worthwhile_to_cache(OpName),!, | |
53 | % assert(operation_read_projection_cache(OpName,no_caching)), | |
54 | % fail. | |
55 | project_in_state(InState,OpName,ProjectedInState ) :- | |
56 | b_get_operation_normalized_read_write_info(OpName,ReadVariables,_WrittenVariables), | |
57 | project_state_onto_vars(InState,ReadVariables,ProjVarsInOrder,RelevantState,0,NrDeleted), | |
58 | (NrDeleted>0 | |
59 | -> assert(operation_read_projection_cache(OpName,ProjVarsInOrder)), | |
60 | %print(proj(OpName,PredResult,RelevantState)),nl, | |
61 | ProjectedInState = RelevantState | |
62 | ; assert(operation_read_projection_cache(OpName,no_caching)), | |
63 | print(not_caching(OpName)),nl, | |
64 | fail). | |
65 | ||
66 | ||
67 | % check if caching is worthwhile for this operation | |
68 | %worthwhile_to_cache(OpName) :- | |
69 | % b_get_operation_normalized_read_write_info(OpName,ReadVariables,WrittenVariables), | |
70 | % length(ReadVariables,NrRV), | |
71 | % bmachine:b_machine_statistics(variables,NrVars), | |
72 | % bmachine:b_machine_statistics(constants,NrConstants), | |
73 | % Perc is (NrRV*100) // (NrVars+NrConstants), | |
74 | % length(WrittenVariables,NrWV), | |
75 | % format('Analyzing ~w, ~w %, read: ~w, written: ~w, tot vars: ~w, tot cst: ~w~n (read: ~w)~n',[OpName,Perc,NrRV,NrWV,NrVars,NrConstants,ReadVariables]), | |
76 | % Perc < 95. % maybe provide as preference | |
77 | ||
78 | :- use_module(library(ordsets),[ord_member/2]). | |
79 | %is_read(ReadVariables,bind(Var,_)) :- ord_member(Var,ReadVariables). | |
80 | ||
81 | % a variation of split_list which also returns a list of predicate results | |
82 | % with re_project_state(L,ProjVarsInOrder,A) : we can split another list using the same pattern | |
83 | ||
84 | project_state_onto_vars([],_,[],[],Acc,Acc). | |
85 | project_state_onto_vars([Elem|Rest],ReadVariables,ProjVars,A,NrDelAcc,NrDel) :- Elem = bind(Var,_), | |
86 | (ord_member(Var,ReadVariables) | |
87 | -> ProjVars=[Var|PT], A=[Elem|AR], ND1=NrDelAcc | |
88 | ; ProjVars=PT, A=AR, ND1 is NrDelAcc+1), | |
89 | project_state_onto_vars(Rest,ReadVariables,PT,AR,ND1,NrDel). | |
90 | ||
91 | % project state(VarsInOrder,State,ProjectedState) | |
92 | % faster than project_state_onto_vars | |
93 | re_project_state([],_,[]). | |
94 | re_project_state([V|PT],[Elem|Rest],A) :- Elem = bind(Var,_), | |
95 | (V==Var -> A=[Elem|AR],re_project_state(PT,Rest,AR) | |
96 | ; re_project_state2(V,PT,Rest,A)). | |
97 | ||
98 | re_project_state2(V,PT,[Elem|Rest],A) :- Elem = bind(Var,_), | |
99 | (V==Var -> A=[Elem|AR],re_project_state(PT,Rest,AR) | |
100 | ; re_project_state2(V,PT,Rest,A)). | |
101 | ||
102 | % ------------------------------- | |
103 | ||
104 | :- use_module(hashing). | |
105 | :- use_module(state_packing). | |
106 | ||
107 | :- dynamic operation_computed/4, operation_cached_results/4. | |
108 | ||
109 | % return Hash and check if operation computed | |
110 | % TO DO: do not hash constants if single value exists ? | |
111 | operation_was_computed(OpName,State,PackedState,Hash,Res) :- | |
112 | pack_values(State,PackedState), | |
113 | hashing:my_term_hash(op(OpName,PackedState),Hash), | |
114 | %terms:term_hash(op(OpName,PackedState),[range(smallint),algorithm(sdbm),depth(infinite),if_var(ignore)],Hash), | |
115 | (operation_computed(Hash,OpName,PackedState,ID) -> Res = ID | |
116 | ; Res= false). | |
117 | ||
118 | get_new_operation_computed_id(OpName,PackedState,Hash,ID) :- | |
119 | inc_counter(op_cache_id,ID), | |
120 | assert(operation_computed(Hash,OpName,PackedState,ID)). | |
121 | ||
122 | :- use_module(probsrc(b_interpreter),[b_execute_top_level_operation_update/5]). | |
123 | % this is used when get_preference(try_operation_reuse,full) | |
124 | compute_operation_on_expanded_store_cache(OpName,Operation,InState,NewState,PathInfo) :- | |
125 | %print(comp(OpName,InState)),nl, | |
126 | project_in_state(InState,OpName,ProjInState), | |
127 | !, | |
128 | %print(proj(OpName,ProjInState)),nl, | |
129 | operation_was_computed(OpName,ProjInState,PackedState,Hash,ID), | |
130 | %print(o(Hash,ID,OpName)),nl, | |
131 | (ID \= false | |
132 | -> %print(cache(OpName,ID)),nl, | |
133 | %print('+'), | |
134 | %hit_profiler:add_hit(operation_reused,OpName), | |
135 | operation_cached_results(ID,Operation,PackedNewState,PathInfo), | |
136 | unpack_values(PackedNewState,NewState) | |
137 | ; get_new_operation_computed_id(OpName,PackedState,Hash,NewID), | |
138 | %print('-'), | |
139 | %hit_profiler:add_hit(operation_cache_computation,OpName), | |
140 | b_execute_top_level_operation_update(OpName,Operation,ProjInState,NewState,PathInfo), | |
141 | pack_values(NewState,PackedNewState), | |
142 | assert(operation_cached_results(NewID,Operation,PackedNewState,PathInfo)) | |
143 | ). | |
144 | compute_operation_on_expanded_store_cache(OpName,Operation,InState,NewState,PathInfo) :- | |
145 | %print(not_caching(OpName)),nl, | |
146 | %hit_profiler:add_hit(operation_not_cached,OpName), | |
147 | b_interpreter:b_execute_top_level_operation_update(OpName,Operation,InState,NewState,PathInfo). | |
148 | ||
149 | ||
150 | % small utility code to analyze hash collisions: | |
151 | % b_operation_cache:ahc. | |
152 | :- public ahc/0. | |
153 | ahc :- operation_computed(Hash,OpName,State,ID), | |
154 | operation_computed(Hash,OpName2,State2,ID2), | |
155 | ID2 > ID, | |
156 | format('Hash collision: ~w~n ~w:~w:~w~n ~w:~w:~w~n~n',[Hash,OpName,State,ID,OpName2,State2,ID2]), | |
157 | fail. | |
158 | ahc. | |
159 | ||
160 | :- use_module(translate,[translate_bstate_limited/2, translate_event_with_limit/3]). | |
161 | :- use_module(debug,[debug_mode/1]). | |
162 | % b_operation_cache:print_op_cache | |
163 | print_op_cache :- \+ operation_computed(_,_,_,_),!. % print nothing | |
164 | print_op_cache :- operation_read_projection_cache(OpName,ProjVars), | |
165 | format('Operation ~w cached onto: ~w~n',[OpName,ProjVars]),fail. | |
166 | print_op_cache :- operation_read_projection_cache(OpName,_), | |
167 | findall(ID,b_operation_cache:operation_computed(_Hash,OpName,_,ID),Ls), length(Ls,Nr), | |
168 | format('Next-state-calls for ~w: ~w~n',[OpName,Nr]),fail. | |
169 | print_op_cache :- | |
170 | findall(ID,b_operation_cache:operation_computed(_Hash,_Op,_,ID),Ls), length(Ls,Nr), | |
171 | format('Total Number of Next-state-calls: ~w~n',[Nr]),fail. | |
172 | print_op_cache :- debug_mode(on), | |
173 | print('Local Operation Cache Info:'),nl, | |
174 | operation_computed(_Hash,OpName,PackedState,ID), | |
175 | format('~nNode ID = ~w, Operation = ~w~n',[ID,OpName]), | |
176 | unpack_values(PackedState,State), translate_bstate_limited(State,NodeDesc), | |
177 | format(' projected state : ~w~n',[NodeDesc]), | |
178 | operation_cached_results(ID,Operation,PackedNewState,_PathInfo), | |
179 | translate_event_with_limit(Operation,100,OpStr), | |
180 | unpack_values(PackedNewState,NewState), translate:translate_bstate_limited(NewState,UpdateStr), | |
181 | format(' ~w -upd-> ~w~n',[OpStr,UpdateStr]), | |
182 | fail. | |
183 | print_op_cache :- ahc,print('----------'),nl. | |
184 | ||
185 | % ---------------------- | |
186 | % DOT RENDERING (not yet finished) | |
187 | ||
188 | %op_cache_node(ID,OpName,NodeDesc,Shape,Style,Color) :- Shape=box, Style=solid, Color=blue, | |
189 | % operation_computed(_Hash,OpName,PackedState,ID), | |
190 | % unpack_values(PackedState,State), translate_bstate_limited(State,NodeDesc). | |
191 | %:- use_module(probsrc(dot_graph_generator),[gen_dot_graph/6]). | |
192 | % Predicates for creating a dependency graph | |
193 | %tcltk_operation_cache_graph(File) :- gen_dot_graph(File,b_operation_cache,op_cache_node,cbc_dependence_trans_predicate,none,none). | |
194 |