| 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 |