The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
calqueue.c
Go to the documentation of this file.
1 
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <limits.h>
46 #include <math.h>
47 #include <stdbool.h>
48 
49 #include <core/core.h>
50 #include <datatypes/calqueue.h>
51 #include <mm/mm.h>
52 
53 // Declare data structures needed for the schedulers
54 static calqueue_node *calq[CALQSPACE]; // Array of linked lists of items
55 static calqueue_node **calendar; // Pointer to use as a sub-array to calq
56 
57 // Global variables for the calendar queueing routines
58 static int firstsub, nbuckets, qsize, lastbucket;
59 static bool resize_enabled;
60 static double top_threshold, bot_threshold, lastprio;
61 
62 static double buckettop, cwidth;
63 
64 static calqueue_node *calqueue_deq(void);
65 
66 /* This initializes a bucket array within the array a[].
67  Bucket width is set equal to bwidth. Bucket[0] is made
68  equal to a[qbase]; and the number of buckets is nbuck.
69  Startprio is the priority at which dequeueing begins.
70  All external variables except resize_enabled are
71  initialized
72 */
73 static void localinit(int qbase, int nbucks, double bwidth, double startprio)
74 {
75  int i;
76  long int n;
77 
78  // Set position and size of nnew queue
79  firstsub = qbase;
80  calendar = calq + qbase;
81  cwidth = bwidth;
82  nbuckets = nbucks;
83 
84  // Init as empty
85  qsize = 0;
86  for (i = 0; i < nbuckets; i++) {
87  calendar[i] = NULL;
88  }
89 
90  // Setup initial position in queue
91  lastprio = startprio;
92  n = (long)((double)startprio / cwidth); // Virtual bucket
93  lastbucket = n % nbuckets;
94  buckettop = (double)(n + 1) * cwidth + 0.5 * cwidth;
95 
96  // Setup queue-size-change thresholds
97  bot_threshold = (int)((double)nbuckets / 2) - 2;
98  top_threshold = 2 * nbuckets;
99 }
100 
101 // This function returns the width that the buckets should have
102 // based on a random sample of the queue so that there will be about 3
103 // items in each bucket.
104 static double new_width(void)
105 {
106  int nsamples, templastbucket, i, j;
107  double templastprio;
108  double tempbuckettop, average, newaverage;
109  calqueue_node *temp[25];
110 
111  // Init the temp node structure
112  for (i = 0; i < 25; i++) {
113  temp[i] = NULL;
114  }
115 
116  // How many queue elements to sample?
117  if (qsize < 2)
118  return 1.0;
119 
120  if (qsize <= 5)
121  nsamples = qsize;
122  else
123  nsamples = 5 + (int)((double)qsize / 10);
124 
125  if (nsamples > 25)
126  nsamples = 25;
127 
128  // Store the current situation
129  templastbucket = lastbucket;
130  templastprio = lastprio;
131  tempbuckettop = buckettop;
132 
133  resize_enabled = false;
134  average = 0.;
135 
136  for (i = 0; i < nsamples; i++) {
137  // Dequeue nodes to get a test sample and sum up the differences in time
138  temp[i] = calqueue_deq();
139  if (likely(i > 0)) {
140  average += temp[i]->timestamp - temp[i - 1]->timestamp;
141  }
142  }
143 
144  // Get the average
145  average = average / (double)(nsamples - 1);
146 
147  newaverage = 0.;
148  j = 0;
149 
150  // Re-insert temp node 0
151  calqueue_put(temp[0]->timestamp, temp[0]->payload);
152 
153  // Recalculate ignoring large separations
154  for (i = 1; i < nsamples; i++) {
155  if ((temp[i]->timestamp - temp[i - 1]->timestamp) < (average * 2.0)) {
156  newaverage += (temp[i]->timestamp - temp[i - 1]->timestamp);
157  j++;
158  }
159  calqueue_put(temp[i]->timestamp, temp[i]->payload);
160  }
161 
162  // Free the temp structure (the events have been re-injected in the queue)
163  for (i = 0; i < 25; i++) {
164  if (temp[i] != NULL) {
165  rsfree(temp[i]);
166  }
167  }
168 
169  // Compute new width
170  newaverage = (newaverage / (double)j) * 3.0; /* this is the new width */
171 
172  // Restore the original condition
173  lastbucket = templastbucket; /* restore the original conditions */
174  lastprio = templastprio;
175  buckettop = tempbuckettop;
176  resize_enabled = true;
177 
178  return newaverage;
179 }
180 
181 // This copies the queue onto a calendar with nnewsize buckets. The new bucket
182 // array is on the opposite end of the array a[QSPACE] from the original EH?!?!?!?!?!
183 static void resize(int newsize)
184 {
185  double bwidth;
186  int i;
187  int oldnbuckets;
188  calqueue_node **oldcalendar, *temp, *temp2;
189 
190  if (!resize_enabled)
191  return;
192 
193  // Find new bucket width
194  bwidth = new_width();
195 
196  // Save location and size of old calendar for use when copying calendar
197  oldcalendar = calendar;
198  oldnbuckets = nbuckets;
199 
200  // Init the new calendar
201  if (firstsub == 0) {
202  localinit(CALQSPACE - newsize, newsize, bwidth, lastprio);
203  } else {
204  localinit(0, newsize, bwidth, lastprio);
205  }
206 
207  // Copy elements to the new calendar
208  for (i = oldnbuckets - 1; i >= 0; --i) {
209  temp = oldcalendar[i];
210  while (temp != NULL) {
211  calqueue_put(temp->timestamp, temp->payload);
212  temp2 = temp->next;
213  rsfree(temp);
214  temp = temp2;
215  }
216  }
217 }
218 
219 static calqueue_node *calqueue_deq(void)
220 {
221  register int i;
222  int temp2;
223  calqueue_node *e;
224  double lowest;
225 
226  // Is there an event to be processed?
227  if (qsize == 0) {
228  return NULL;
229  }
230 
231  for (i = lastbucket;;) {
232  // Check bucket i
233  if (calendar[i] != NULL && calendar[i]->timestamp < buckettop) {
234 
235  calendar_process:
236 
237  // Found an item to be processed
238  e = calendar[i];
239 
240  // Update position on calendar and queue's size
241  lastbucket = i;
242  lastprio = e->timestamp;
243  qsize--;
244 
245  // Remove the event from the calendar queue
246  calendar[i] = calendar[i]->next;
247 
248  // Halve the calendar size if needed
249  if (qsize < bot_threshold)
250  resize((int)((double)nbuckets / 2));
251 
252  // Processing completed
253  return e;
254 
255  } else {
256  // Prepare to check the next bucket, or go to a direct search
257  i++;
258  if (i == nbuckets)
259  i = 0;
260 
261  buckettop += cwidth;
262 
263  if (i == lastbucket)
264  break; // Go to direct search
265  }
266  }
267 
268  // Directly search for minimum priority event
269  temp2 = -1;
270  lowest = (double)LLONG_MAX;
271  for (i = 0; i < nbuckets; i++) {
272  if ((calendar[i] != NULL)
273  && ((temp2 == -1) || (calendar[i]->timestamp < lowest))) {
274  temp2 = i;
275  lowest = calendar[i]->timestamp;
276  }
277  }
278 
279  // Process the event found and and handle the queue
280  i = temp2;
281  goto calendar_process;
282 
283 }
284 
285 // This function initializes the messaging queue.
286 void calqueue_init(void)
287 {
288  localinit(0, 2, 1.0, 0.0);
289  resize_enabled = true;
290 }
291 
292 void calqueue_put(double timestamp, void *payload)
293 {
294  int i;
295  calqueue_node *new_node, *traverse;
296 
297  // Fill the node entry
298  new_node = rsalloc(sizeof(calqueue_node));
299  new_node->timestamp = timestamp;
300  new_node->payload = payload;
301  new_node->next = NULL;
302 
303  // Calculate the number of the bucket in which to place the new entry
304  i = (int)round(timestamp / (double)cwidth); // Virtual bucket
305  i = i % nbuckets; // Actual bucket
306 
307  // Grab the head of events list in bucket i
308  traverse = calendar[i];
309 
310  // Put at the head of the list, if appropriate
311  if (traverse == NULL || traverse->timestamp > timestamp) {
312  new_node->next = traverse;
313  calendar[i] = new_node;
314  } else {
315  // Find the correct place in list
316  while (traverse->next != NULL
317  && traverse->next->timestamp <= timestamp)
318  traverse = traverse->next;
319 
320  // Place the new event
321  new_node->next = traverse->next;
322  traverse->next = new_node;
323  }
324 
325  // Update queue size
326  qsize++;
327 
328  // Double the calendar size if needed
329  if (qsize > top_threshold && nbuckets < MAXNBUCKETS) {
330  resize(2 * nbuckets);
331  }
332 }
333 
334 void *calqueue_get(void)
335 {
336  calqueue_node *node;
337  void *payload;
338 
339  node = calqueue_deq();
340  if (unlikely(node == NULL)) {
341  return NULL;
342  }
343 
344  payload = node->payload;
345  rsfree(node);
346  return payload;
347 }
#define likely(exp)
Optimize the branch as likely taken.
Definition: core.h:72
Core ROOT-Sim functionalities.
Memory Manager main header.
Calendar Queue Implementation.
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74