The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
obstacles.c
1 /*
2  * topology_binary.c
3  *
4  * Created on: 02 ago 2018
5  * Author: andrea
6  */
7 
8 #include <ROOT-Sim.h>
9 #include <lib/topology.h>
10 
11 #include <math.h>
12 
13 #include <scheduler/scheduler.h>
14 #include <lib/jsmn_helper.h>
15 #include <lib/numerical.h>
16 #include <datatypes/bitmap.h>
17 #include <datatypes/heap.h>
18 #include <scheduler/process.h>
19 
21 typedef struct _topology_t {
22  unsigned neighbours_id[6];
23  unsigned free_neighbours;
24  unsigned *prev_next_cache;
25  bool dirty;
27 } topology_t;
28 
29 unsigned size_checkpoint_obstacles(void){
30  return sizeof(topology_t) + // the basic struct size
31  bitmap_required_size(topology_global.lp_cnt) + // the bitmap to hold obstacles
32  sizeof(unsigned) * 2 * topology_global.lp_cnt; // the cache for next and previous hops
33 }
34 
35 // this is called once per machine after the general
36 void *load_topology_file_obstacles(c_jsmntok_t *root_token, const char *json_base){
37  unsigned i;
38  double tmp;
39  const unsigned lp_cnt = topology_global.lp_cnt;
40  // retrieve the values array checking its size against the lp_cnt in the topology
41  c_jsmntok_t *values_tok = get_value_token_by_key(root_token, json_base, root_token, "values");
42  if(!values_tok|| values_tok->type != JSMN_ARRAY || children_count_token(root_token, values_tok) != lp_cnt)
43  rootsim_error(true, "Invalid or missing json value with key \"values\"");
44  // we initialize the machine shared initial obstacles status
45  rootsim_bitmap *ret_data = rsalloc(bitmap_required_size(lp_cnt));
46  bitmap_initialize(ret_data, lp_cnt);
47  // now we parse the json array to fill in the actual data
48  struct _gnt_closure_t closure = GNT_CLOSURE_INITIALIZER;
49  for (i = 0; i < lp_cnt; ++i) {
50  if(parse_double_token(json_base, get_next_token(root_token, values_tok, &closure), &tmp) < 0)
51  rootsim_error(true, "Invalid value found in the \"value\" array");
52  // we interpret ones as obstacles, we use the double comparison to avoid warnings
53  if(tmp >= 1.0 && tmp <= 1.0) {bitmap_set(ret_data, i);}
54  }
55  return ret_data;
56 }
57 
58 
59 topology_t *topology_obstacles_init(unsigned this_region_id, void *topology_data){
60  unsigned i, lp_id;
61  const unsigned lp_cnt = topology_global.lp_cnt;
62 
63  // allocate the topology struct, we use a single allocation for all the stuff we need
64  topology_t* topology = rsalloc(topology_global.chkp_size);
65 
66  topology->prev_next_cache = (unsigned *)(((char *)topology->data) + bitmap_required_size(lp_cnt));
67 
68  if(topology_data){
69  memcpy(topology->data, topology_data, bitmap_required_size(lp_cnt));
70  }else{
71  bitmap_initialize(topology->data, lp_cnt);
72  }
73 
74  i = topology_global.directions;
75  topology->free_neighbours = i;
76  if(topology_global.geometry != TOPOLOGY_GRAPH){
77  // we save the neighbours ids for faster accessing
78  while(i--){
79  lp_id = get_raw_receiver(this_region_id, i);
80  topology->neighbours_id[i] = lp_id;
81  // we also count reachable neighbours
82  if(lp_id == DIRECTION_INVALID || bitmap_check(topology->data, lp_id))
83  topology->free_neighbours--;
84  }
85  }else{
86  // in a graph directions are 1 to 1 with regions
87  while(i--)
88  if(this_region_id == i || bitmap_check(topology->data, i))
89  topology->free_neighbours--;
90  }
91 
92  topology->dirty = true;
93 
94  return topology;
95 }
96 
97 #define __cmp_dijkstra_h(a, b) (((a).hops > (b).hops) - ((b).hops > (a).hops))
98 // this is costly: we try as much as possible to cache the results of this function
99 static void dijkstra_obstacles(const topology_t *topology, unsigned int source_cell, unsigned int previous[RegionsCount()]) {
100 
101  // helper structure, we use this as heap elements to keep track of vertexes status during dijkstra execution
102  struct _dijkstra_h_t{
103  unsigned hops;
104  unsigned cell;
105  };
106 
107  const unsigned directions = topology_global.directions;
108  const unsigned lp_cnt = topology_global.lp_cnt;
109  unsigned i, receiver;
110  unsigned min_costs[lp_cnt];
111  rootsim_heap(struct _dijkstra_h_t) heap;
112  const rootsim_bitmap *obstacles = topology->data;
113 
114  struct _dijkstra_h_t current_scan = {0, source_cell}, partial_scan = {0};
115  // initialize regions as unreachable
116  i = lp_cnt;
117  while(i--){
118  min_costs[i] = UINT_MAX;
119  previous[i] = UINT_MAX;
120  }
121  // heap init
122  heap_init(heap);
123  // the source cell has distance 0
124  min_costs[source_cell] = current_scan.hops;
125  // textbook dijkstra (keep in mind i'm not passing pointers, this stuff gets copied)
126  heap_insert(heap, current_scan, __cmp_dijkstra_h);
127  // while we have vertexes to process
128  while(!heap_empty(heap)) {
129  // extract the lowest one
130  current_scan = heap_extract(heap, __cmp_dijkstra_h);
131  // since we are not supporting decrease key on the heap we have to filter spurious duplicates
132  if(current_scan.hops > min_costs[current_scan.cell])
133  continue;
134  // we compute the sum of the current distance plus one hop to the receiver
135  partial_scan.hops = current_scan.hops + 1;
136  // we cycle through the neighbours of the current cell
137  for(i = 0; i < directions; ++i){
138  // we get the receiver cell
139  receiver = get_raw_receiver(current_scan.cell, i);
140  // obviously we want a valid neighbour
141  if(receiver == DIRECTION_INVALID || bitmap_check(obstacles, receiver))
142  continue;
143  // if lower we have a candidate optimum for the cell
144  if(partial_scan.hops < min_costs[receiver]){
145  // set the previous cell to retrieve the path later on
146  previous[receiver] = current_scan.cell;
147  // we set the cell field on our struct
148  partial_scan.cell = receiver;
149  // refresh lowest cost found for the cell
150  min_costs[receiver] = partial_scan.hops;
151  // we insert this into the heap
152  heap_insert(heap, partial_scan, __cmp_dijkstra_h);
153  }
154  }
155  }
156 }
157 #undef __cmp_dijsktra_h
158 
159 static void refresh_cache_obstacles(topology_t *topology){
160  if(topology->dirty){
161  const unsigned lp_cnt = topology_global.lp_cnt;
162  // calculate the minimum costs spanning tree
163  dijkstra_obstacles(topology, current->gid.to_int, topology->prev_next_cache);
164  // this sets to an uninitialized value the buffer which holds the next hop for
165  // each possible destination (we compute those on demand when asked by the user and we cache those here)
166  memset(&topology->prev_next_cache[lp_cnt], UCHAR_MAX, sizeof(unsigned) * lp_cnt);
167  topology->dirty = false;
168  }
169 }
170 
171 unsigned int find_receiver_obstacles(void) {
172  const topology_t *topology = current->topology;
173  const rootsim_bitmap *obstacles = topology->data;
174  const unsigned sender = current->gid.to_int;
175  if(unlikely(bitmap_check(obstacles, sender))){
176  rootsim_error(false, "FindReceiver(): this is an obstacle region!!!\n");
177  return DIRECTION_INVALID;
178  }
179 
180  if(unlikely(!topology->free_neighbours))
181  return sender;
182 
183  const unsigned directions = topology_global.directions;
184  const unsigned *neighbours;
185  unsigned receiver;
186 
187  switch(topology_global.geometry){
188  case TOPOLOGY_HEXAGON:
189  case TOPOLOGY_SQUARE:
190  case TOPOLOGY_TORUS:
191  case TOPOLOGY_BIDRING:
192  case TOPOLOGY_RING:
193  neighbours = topology->neighbours_id;
194  do{
195  receiver = neighbours[(unsigned)(directions * Random())];
196  }while(unlikely(receiver == DIRECTION_INVALID || bitmap_check(obstacles, receiver)));
197  break;
198 
199  case TOPOLOGY_GRAPH:
200  do{
201  receiver = (directions + 1) * Random();
202  }while(unlikely(bitmap_check(obstacles, receiver)));
203  break;
204 
205  case TOPOLOGY_STAR:
206  if(sender != 0) {
207  receiver = 0;
208  } else {
209  receiver = ((topology_global.lp_cnt - 1) * Random()) + 1;
210  }
211  }
212  return receiver;
213 }
214 
215 static inline void toggle_bit_and_state(topology_t *topology, rootsim_bitmap *bitmap, unsigned from){
216 
217  unsigned refresh_free = 0;
218  if(topology_global.geometry != TOPOLOGY_GRAPH){
219  unsigned i = topology_global.directions;
220  while(i--)
221  if(topology->neighbours_id[i] != DIRECTION_INVALID){
222  refresh_free = 1;
223  break;
224  }
225  }else{
226  refresh_free = (from != current->gid.to_int);
227  }
228 
229 
230  if(bitmap_check(bitmap, from)){
231  bitmap_reset(bitmap, from);
232  topology->free_neighbours += refresh_free;
233  }else{
234  bitmap_set(bitmap, from);
235  topology->free_neighbours -= refresh_free;
236  }
237  topology->dirty = true;
238 }
239 
240 void set_value_topology_obstacles(unsigned from, unsigned to, double value){
241  (void)to;
242 
243  topology_t *topology = current->topology;
244  const unsigned this_lp = current->gid.to_int;
245  rootsim_bitmap *bitmap = topology->data;
246 
247  if(!((value > 0) ^ bitmap_check(bitmap, from)))
248  return;
249 
250  toggle_bit_and_state(topology, bitmap, from);
251 
252  unsigned i = topology_global.lp_cnt;
253  while(i--){
254  if(i == this_lp)
255  continue;
256  UncheckedScheduleNewEvent(i, current_evt->timestamp, TOPOLOGY_UPDATE, &from, sizeof(from));
257  }
258 }
259 
260 void update_topology_obstacles(void){
261  topology_t *topology = current->topology;
262  toggle_bit_and_state(topology, topology->data, *((unsigned *)current_evt->event_content));
263 }
264 
265 double get_value_topology_obstacles(unsigned from, unsigned to){
266  (void)to;
267  return bitmap_check(current->topology->data, from) ? 1.0 : 0.0;
268 }
269 
270 bool is_reachable_obstacles(unsigned to){
271  // XXX do we want deep reachability or dumb reachability?
272  return find_receiver_toward_obstacles(to) != UINT_MAX;
273 }
274 
275 unsigned int find_receiver_toward_obstacles(unsigned int to){
276  topology_t *topology = current->topology;
277  const unsigned lp_cnt = topology_global.lp_cnt;
278  const unsigned this_lp = current->gid.to_int;
279 
280  refresh_cache_obstacles(topology);
281 
282  unsigned *ret_p = &topology->prev_next_cache[lp_cnt + to];
283 
284  if(*ret_p == UINT_MAX){
285  // this value hasn't been requested yet
286  unsigned path[lp_cnt];
287  // we compute the complete path
288  if(!build_path(lp_cnt, path, topology->prev_next_cache, this_lp, to)){
289  // there's no path so we put in a sentinel value
290  *ret_p = this_lp;
291  }else{
292  // we got a feasible path, we save the first hop
293  *ret_p = path[0];
294  }
295  }
296 
297  if(*ret_p == this_lp){
298  // the requested cell is unreachable
299  return UINT_MAX;
300  }
301 
302  return *ret_p;
303 }
304 
305 double compute_min_tour_obstacles(unsigned int source, unsigned int dest, unsigned int result[RegionsCount()]) {
306  topology_t *topology = current->topology;
307  const unsigned lp_cnt = topology_global.lp_cnt;
308  unsigned int previous[lp_cnt], hops;
309 
310  if(source == current->gid.to_int){
311  refresh_cache_obstacles(topology);
312  if(!(hops = build_path(lp_cnt, result, topology->prev_next_cache, source, dest))){
313  // the requested cell isn't reachable: this is a sentinel value indicating impossibility
314  topology->prev_next_cache[lp_cnt + dest] = source;
315  return -1.0;
316  }
317  topology->prev_next_cache[lp_cnt + dest] = result[0]; // we cache the next hop value;
318  return hops;
319  }
320 
321  dijkstra_obstacles(topology, source, previous);
322 
323  if(!(hops = build_path(lp_cnt, result, previous, source, dest)))
324  return -1.0;
325 
326  return hops;
327 }
unsigned neighbours_id[6]
Definition: obstacles.c:22
Definition: jsmn.h:43
#define bitmap_set(bitmap, bit_index)
This sets the bit with index bit_index of the bitmap bitmap.
Definition: bitmap.h:116
enum _topology_geometry_t geometry
Definition: topology.h:22
A generic invalid direction.
Definition: ROOT-Sim.h:122
unsigned free_neighbours
Definition: obstacles.c:23
square grid topology
Definition: ROOT-Sim.h:91
ROOT-Sim header for model development.
#define bitmap_required_size(requested_bits)
Computes the required size of a bitmap with requested_bits entries.
Definition: bitmap.h:92
unsigned chkp_size
Definition: topology.h:18
unsigned int to_int
The GID numerical value.
Definition: core.h:133
unsigned lp_cnt
Definition: topology.h:21
The ROOT-Sim scheduler main module header.
#define bitmap_reset(bitmap, bit_index)
This resets the bit with index bit_index of the bitmap bitmap.
Definition: bitmap.h:125
a torus shaped grid topology (a wrapping around square topology)
Definition: ROOT-Sim.h:94
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
double data[]
a pointer to the cache used to speedup queries on paths (unused in probabilities topology type) ...
Definition: costs.c:25
Numerical Library.
hexagonal grid topology
Definition: ROOT-Sim.h:90
__thread msg_t * current_evt
Definition: scheduler.c:83
topology_t * topology
pointer to the topology struct
Definition: process.h:152
unsigned directions
Definition: topology.h:19
LP control blocks.
an arbitrary shaped topology
Definition: ROOT-Sim.h:96
a ring shaped topology direction
Definition: ROOT-Sim.h:93
GID_t gid
Global ID of the LP.
Definition: process.h:82
bool dirty
Definition: costs.c:23
#define bitmap_check(bitmap, bit_index)
This checks if the bit with index bit_index of the bitmap bitmap is set or unset. ...
Definition: bitmap.h:135
Bitmap data type.
a ring shaped topology walkable in a single direction
Definition: ROOT-Sim.h:92
unsigned * prev_next_cache
Definition: costs.c:24
#define bitmap_initialize(memory_pointer, requested_bits)
This initializes the bitmap at memory_pointer containing requested_bits entries.
Definition: bitmap.h:107
double Random(void)
Definition: numerical.c:80
unsigned char rootsim_bitmap
This defines a generic bitmap.
Definition: bitmap.h:43
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74
the customised struct for TOPOLOGY_OBSTACLES representation
Definition: costs.c:22