The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
statistics.c
Go to the documentation of this file.
1 
54 #include <unistd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <errno.h>
59 #include <time.h>
60 #include <math.h>
61 #include <sys/stat.h>
62 #include <sys/types.h>
63 #include <dirent.h>
64 
65 #include <arch/thread.h>
66 #include <arch/memusage.h>
67 #include <scheduler/process.h>
68 #include <scheduler/scheduler.h>
69 #include <gvt/gvt.h>
70 #include <statistics/statistics.h>
71 #include <queues/queues.h>
72 #include <mm/state.h>
73 #include <mm/mm.h>
74 #include <core/core.h>
75 #include <core/init.h>
76 #include <core/timer.h>
77 #ifdef HAVE_MPI
78 #include <communication/mpi.h>
79 #endif
80 
81 #define GVT_BUFF_ROWS 50
82 
83 struct _gvt_buffer {
84  unsigned int pos;
86  double exec_time;
87  double gvt;
88  unsigned committed;
89  unsigned cumulated;
90  }rows[GVT_BUFF_ROWS];
91 };
92 
94 static __thread struct _gvt_buffer gvt_buf = {0};
95 
97 static FILE **thread_blob_files = {0};
99 static FILE *unique_files[NUM_STAT_FILE_U] = {0};
101 static FILE **thread_files[NUM_STAT_FILE_T] = {0};
102 
104 static timer simulation_timer;
105 
107 static struct stat_t *lp_stats;
108 
110 static struct stat_t *lp_stats_gvt;
111 
113 static struct stat_t *thread_stats;
114 
116 static struct stat_t system_wide_stats = {.gvt_round_time_min = INFTY};
117 
118 #ifdef HAVE_MPI
119 struct stat_t global_stats = {.gvt_round_time_min = INFTY};
121 #endif
122 
136 #define safe_asprintf(ret_addr, format, ...) ({ \
137  char *__pstr = NULL; \
138  int __ret = snprintf(0, 0, format, ##__VA_ARGS__); \
139  if(__ret < 0) \
140  rootsim_error(true, "Error in snprintf()!"); \
141  __pstr = rsalloc(__ret + 1); \
142  __ret = snprintf(__pstr, __ret + 1, format, ##__VA_ARGS__); \
143  if(__ret < 0) \
144  rootsim_error(true, "Error in snprintf()!"); \
145  *(ret_addr) = __pstr; \
146 })
147 
148 
154 static void _rmdir(const char *path)
155 {
156  char *buf;
157  struct stat st;
158  struct dirent *dirt;
159  DIR *dir;
160 
161  // We could be asked to remove a non-existing folder. In that case, do nothing!
162  if (!(dir = opendir(path))) {
163  return;
164  }
165 
166  while ( (dirt = readdir(dir))) {
167  if ((strcmp(dirt->d_name, ".") == 0) || (strcmp(dirt->d_name, "..") == 0))
168  continue;
169 
170  safe_asprintf(&buf, "%s/%s", path, dirt->d_name);
171 
172  if (stat(buf, &st) == -1) {
173  rootsim_error(false, "stat() file \"%s\" failed, %s\n", buf, strerror(errno));
174  }else if (S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)) {
175  if (unlink(buf) == -1)
176  rootsim_error(false, "unlink() file \"%s\" failed, %s\n", buf, strerror(errno));
177  }
178  else if (S_ISDIR(st.st_mode)) {
179  _rmdir(buf);
180  }
181 
182  rsfree(buf);
183  }
184  closedir(dir);
185 
186  if (rmdir(path) == -1)
187  rootsim_error(false, "rmdir() directory \"%s\" failed, %s\n", path, strerror(errno));
188 }
189 
190 
198 void _mkdir(const char *path)
199 {
200  char opath[MAX_PATHLEN];
201  char *p;
202  size_t len;
203 
204  strncpy(opath, path, sizeof(opath));
205  opath[MAX_PATHLEN - 1] = '\0';
206  len = strlen(opath);
207  if(opath[len - 1] == '/')
208  opath[len - 1] = '\0';
209 
210  // opath plus 1 is a hack to allow also absolute path
211  for(p = opath + 1; *p; p++) {
212  if(*p == '/') {
213  *p = '\0';
214  if(access(opath, F_OK))
215  if (mkdir(opath, S_IRWXU))
216  if (errno != EEXIST) {
217  rootsim_error(true, "Could not create output directory", opath);
218  }
219  *p = '/';
220  }
221  }
222 
223  // Path does not terminate with a slash
224  if(access(opath, F_OK)) {
225  if (mkdir(opath, S_IRWXU)) {
226  if (errno != EEXIST) {
227  if (errno != EEXIST) {
228  rootsim_error(true, "Could not create output directory", opath);
229  }
230  }
231  }
232  }
233 }
234 
235 static void print_config_to_file(FILE *f)
236 {
237  fprintf(f,
238  "****************************\n"
239  "* ROOT-Sim Configuration *\n"
240  "****************************\n"
241  "Kernels: %u\n"
242  "Cores: %ld available, %d used\n"
243  "Number of Logical Processes: %u\n"
244  "Output Statistics Directory: %s\n"
245  "Scheduler: %s\n"
246  #ifdef HAVE_MPI
247  "MPI multithread support: %s\n"
248  #endif
249  "GVT Time Period: %.2f seconds\n"
250  "Checkpointing Type: %s\n"
251  "Checkpointing Period: %d\n"
252  "Snapshot Reconstruction Type: %s\n"
253  "Halt Simulation After: %d\n"
254  "LPs Distribution Mode across Kernels: %s\n"
255  "Check Termination Mode: %s\n"
256  "Set Seed: %ld\n",
257  n_ker,
258  get_cores(),
259  n_cores,
260  n_prc_tot,
262  param_to_text[PARAM_SCHEDULER][rootsim_config.scheduler],
263  #ifdef HAVE_MPI
264  ((mpi_support_multithread)? "yes":"no"),
265  #endif
267  param_to_text[PARAM_STATE_SAVING][rootsim_config.checkpointing],
269  param_to_text[PARAM_SNAPSHOT][rootsim_config.snapshot],
271  param_to_text[PARAM_LPS_DISTRIBUTION][rootsim_config.lps_distribution],
274 }
275 
276 
277 void print_config(void) {
278  if(master_kernel())
279  print_config_to_file(stdout);
280 }
281 
282 
287 {
288  timer_start(simulation_timer);
289 }
290 
291 
298 static char *format_size(double size)
299 {
300  static __thread char size_str[32];
301  char *fmt;
302  int divisor;
303 
304  if(size <= 1024) {
305  fmt = "%.02f B";
306  divisor = 1;
307  } else if (size <= 1024 * 1024) {
308  fmt = "%.02f KB";
309  divisor = 1024;
310  } else if (size <= 1024 * 1024 * 1024) {
311  fmt = "%.02f MB";
312  divisor = 1024 * 1024;
313  } else {
314  fmt = "%.02f GB";
315  divisor = 1024 * 1024 * 1024;
316  }
317 
318  snprintf(size_str, 32, fmt, size / divisor);
319 
320  return size_str;
321 }
322 
323 #define HEADER_STR "------------------------------------------------------------"
324 static void print_header(FILE *f, const char *title)
325 {
326  char buf[sizeof(HEADER_STR)] = HEADER_STR;
327  unsigned title_len = strlen(title);
328  assert(title_len < sizeof(HEADER_STR));
329  memcpy(buf + ((sizeof(HEADER_STR) - title_len) / 2), title, title_len);
330  fprintf(f, HEADER_STR "\n%s\n" HEADER_STR "\n\n", buf);
331 }
332 
333 
334 static void print_timer_stats(FILE *f, timer *start_timer, timer *stop_timer, double total_time)
335 {
336  char timer_string[TIMER_BUFFER_LEN];
337  timer_tostring(*start_timer, timer_string);
338  fprintf(f, "SIMULATION STARTED AT ..... : %s \n", timer_string);
339  timer_tostring(*stop_timer, timer_string);
340  fprintf(f, "SIMULATION FINISHED AT .... : %s \n", timer_string);
341  fprintf(f, "TOTAL SIMULATION TIME ..... : %.03f seconds \n\n", total_time);
342 }
343 
344 
345 static void print_common_stats(FILE *f, struct stat_t *stats_p, bool want_thread_stats, bool want_local_stats)
346 {
347  double rollback_frequency = (stats_p->tot_rollbacks / stats_p->tot_events);
348  double rollback_length = (stats_p->tot_rollbacks > 0 ? (stats_p->tot_events - stats_p->committed_events) / stats_p->tot_rollbacks : 0);
349  double efficiency = (1 - rollback_frequency * rollback_length) * 100;
350 
351  fprintf(f, "TOTAL KERNELS ............. : %d \n", n_ker);
352  if(want_local_stats) {
353  fprintf(f, "KERNEL ID ................. : %d \n", kid);
354  fprintf(f, "LPs HOSTED BY KERNEL....... : %d \n", n_prc);
355  fprintf(f, "TOTAL_THREADS ............. : %d \n", n_cores);
356  } else {
357  fprintf(f, "TOTAL_THREADS ............. : %d \n", n_cores * n_ker);
358  fprintf(f, "TOTAL LPs.................. : %d \n", n_prc_tot);
359  }
360  if(want_thread_stats) {
361  fprintf(f, "THREAD ID ................. : %d \n", local_tid);
362  fprintf(f, "LPs HOSTED BY THREAD ...... : %d \n", n_prc_per_thread);
363  }
364  fprintf(f, "\n");
365  fprintf(f, "TOTAL EXECUTED EVENTS ..... : %.0f \n", stats_p->tot_events);
366  fprintf(f, "TOTAL COMMITTED EVENTS..... : %.0f \n", stats_p->committed_events);
367  fprintf(f, "TOTAL REPROCESSED EVENTS... : %.0f \n", stats_p->reprocessed_events);
368  fprintf(f, "TOTAL ROLLBACKS EXECUTED... : %.0f \n", stats_p->tot_rollbacks);
369  fprintf(f, "TOTAL ANTIMESSAGES......... : %.0f \n", stats_p->tot_antimessages);
370  fprintf(f, "ROLLBACK FREQUENCY......... : %.2f %%\n", rollback_frequency * 100);
371  fprintf(f, "ROLLBACK LENGTH............ : %.2f events\n", rollback_length);
372  fprintf(f, "EFFICIENCY................. : %.2f %%\n", efficiency);
373  fprintf(f, "AVERAGE EVENT COST......... : %.2f us\n", stats_p->event_time / stats_p->tot_events);
374  fprintf(f, "AVERAGE EVENT COST (EMA)... : %.2f us\n", stats_p->exponential_event_time);
375  fprintf(f, "AVERAGE CHECKPOINT COST.... : %.2f us\n", stats_p->ckpt_time / stats_p->tot_ckpts);
376  fprintf(f, "AVERAGE RECOVERY COST...... : %.2f us\n", (stats_p->tot_recoveries > 0 ? stats_p->recovery_time / stats_p->tot_recoveries : 0));
377  fprintf(f, "AVERAGE LOG SIZE........... : %s\n", format_size(stats_p->ckpt_mem / stats_p->tot_ckpts));
378  fprintf(f, "\n");
379  fprintf(f, "IDLE CYCLES................ : %.0f\n", stats_p->idle_cycles);
380  if(!want_thread_stats){
381  fprintf(f, "LAST COMMITTED GVT ........ : %f\n", get_last_gvt());
382  }
383  fprintf(f, "NUMBER OF GVT REDUCTIONS... : %.0f\n", stats_p->gvt_computations);
384  if(!want_thread_stats && n_ker > 1){
385  fprintf(f, "MIN GVT ROUND TIME......... : %.2f us\n", stats_p->gvt_round_time_min);
386  fprintf(f, "MAX GVT ROUND TIME......... : %.2f us\n", stats_p->gvt_round_time_max);
387  fprintf(f, "AVERAGE GVT ROUND TIME..... : %.2f us\n", stats_p->gvt_round_time / stats_p->gvt_computations);
388  }
389  fprintf(f, "SIMULATION TIME SPEED...... : %.2f units per GVT\n",stats_p->simtime_advancement);
390  fprintf(f, "AVERAGE MEMORY USAGE....... : %s\n", format_size(stats_p->memory_usage / stats_p->gvt_computations));
391  if(!want_thread_stats)
392  fprintf(f, "PEAK MEMORY USAGE.......... : %s\n", format_size(stats_p->max_resident_set));
393 }
394 
395 
396 static void print_termination_status(FILE *f, int exit_code)
397 {
398  switch(exit_code){
399  case EXIT_SUCCESS:
400  fprintf(f, "\n--------- SIMULATION CORRECTLY COMPLETED ----------\n");
401  break;
402  case EXIT_FAILURE:
403  default:
404  fprintf(f, "\n--------- SIMULATION ABNORMALLY TERMINATED ----------\n");
405  }
406 }
407 
408 void print_gvt_stats_file(void)
409 {
410  size_t elems, i;
411  FILE *f_blob = thread_blob_files[local_tid];
412  FILE *f_final = thread_files[STAT_FILE_T_GVT][local_tid];
413  // last flush of our buffer in the blob file
414  fwrite(gvt_buf.rows, sizeof(struct _gvt_buffer_row_t), gvt_buf.pos, f_blob);
415  // print the header
416  fprintf(f_final, "#%15.15s %15.15s ", "\"WCT\"", "\"GVT VALUE\"");
417  fprintf(f_final, "%15.15s %15.15s\n", "\"EVENTS\"", "\"CUMUL EVENTS\"");
418  // prepare to read back the blob
419  rewind(f_blob);
420  do {
421  // read the blob file back to the buffer piece to piece
422  elems = fread(gvt_buf.rows, sizeof(struct _gvt_buffer_row_t), GVT_BUFF_ROWS, f_blob);
423  for(i = 0; i < elems; ++i) {
424  // write line per line
425  fprintf(f_final, " %15lf %15lf ", gvt_buf.rows[i].exec_time, gvt_buf.rows[i].gvt);
426  fprintf(f_final, "%15u %15u\n", gvt_buf.rows[i].committed, gvt_buf.rows[i].cumulated);
427  }
428  } while(!feof(f_blob));
429  fflush(f_final);
430 }
431 
442 void statistics_stop(int exit_code)
443 {
444  register unsigned int i;
445  FILE *f;
446  double total_time;
447  timer simulation_finished;
448 
449  if(rootsim_config.serial) {
450  f = unique_files[STAT_FILE_U_GLOBAL];
451 
452  // Stop timers
453  timer_start(simulation_finished);
454  total_time = timer_value_seconds(simulation_timer);
455 
456  print_header(f, "SERIAL STATISTICS");
457  print_timer_stats(f, &simulation_timer, &simulation_finished, total_time);
458  fprintf(f, "TOTAL LPs.................. : %d \n", n_prc_tot);
459  fprintf(f, "TOTAL EXECUTED EVENTS ..... : %.0f \n", system_wide_stats.tot_events);
460  fprintf(f, "AVERAGE EVENT COST......... : %.3f us\n", total_time / system_wide_stats.tot_events * 1000 * 1000);
461  fprintf(f, "AVERAGE EVENT COST (EMA)... : %.2f us\n", system_wide_stats.exponential_event_time);
462  fprintf(f, "\n");
463  fprintf(f, "LAST COMMITTED GVT ........ : %f\n", system_wide_stats.gvt_time);
464  fprintf(f, "SIMULATION TIME SPEED...... : %.2f units per GVT\n",system_wide_stats.simtime_advancement);
465  fprintf(f, "AVERAGE MEMORY USAGE....... : %s\n", format_size(system_wide_stats.memory_usage / system_wide_stats.gvt_computations));
466  fprintf(f, "PEAK MEMORY USAGE.......... : %s\n", format_size(getPeakRSS()));
467  print_termination_status(f, exit_code);
468  fflush(f);
469 
470  print_termination_status(stdout, exit_code);
471 
472  } else { /* Parallel simulation */
473 
474  // Stop the simulation timer immediately to avoid considering the statistics reduction time
475  if (master_thread()) {
476  timer_start(simulation_finished);
477  total_time = timer_value_seconds(simulation_timer);
478  }
479 
481  print_gvt_stats_file();
482 
483  /* dump per-LP statistics if required. They are already reduced during the GVT phase */
485  f = thread_files[STAT_FILE_T_LP][local_tid];
486 
487  fprintf(f, "#%15.15s %15.15s %15.15s %15.15s", "\"GID\"", "\"LID\"", "\"TOTAL EVENTS\"", "\"COMM EVENTS\"");
488  fprintf(f, " %15.15s %15.15s %15.15s %15.15s", "\"REPROC EVENTS\"", "\"ROLLBACKS\"", "\"ANTIMSG\"", "\"AVG EVT COST\"");
489  fprintf(f, " %15.15s %15.15s %15.15s\n", "\"AVG CKPT COST\"", "\"AVG REC COST\"", "\"IDLE CYCLES\"");
490 
491  foreach_bound_lp(lp) {
492  unsigned int lp_id = lp->lid.to_int;
493 
494  fprintf(f, " %15u ", lp->gid.to_int);
495  fprintf(f, "%15u ", lp_id);
496  fprintf(f, "%15.0lf ", lp_stats[lp_id].tot_events);
497  fprintf(f, "%15.0lf ", lp_stats[lp_id].committed_events);
498  fprintf(f, "%15.0lf ", lp_stats[lp_id].reprocessed_events);
499  fprintf(f, "%15.0lf ", lp_stats[lp_id].tot_rollbacks);
500  fprintf(f, "%15.0lf ", lp_stats[lp_id].tot_antimessages);
501  fprintf(f, "%15lf ", lp_stats[lp_id].event_time / lp_stats[lp_id].tot_events);
502  fprintf(f, "%15.0lf ", lp_stats[lp_id].ckpt_time / lp_stats[lp_id].tot_ckpts);
503  fprintf(f, "%15.0lf ", (lp_stats[lp_id].tot_rollbacks > 0 ? lp_stats[lp_id].recovery_time / lp_stats[lp_id].tot_recoveries : 0));
504  fprintf(f, "%15.0lf ", lp_stats[lp_id].idle_cycles);
505  fprintf(f, "\n");
506  }
507  }
508 
509  /* Reduce and dump per-thread statistics */
510 
511  // Sum up all LPs statistics
512  foreach_bound_lp(lp) {
513  thread_stats[local_tid].vec += lp_stats[lp->lid.to_int].vec;
514  }
515  thread_stats[local_tid].exponential_event_time /= n_prc_per_thread;
516 
517  // Compute derived statistics and dump everything
518  f = thread_files[STAT_FILE_T_THREAD][local_tid];
519  print_header(f, "THREAD STATISTICS");
520  print_common_stats(f, &thread_stats[local_tid], true, true);
521  print_termination_status(f, exit_code);
522  fflush(f);
523 
525 
526  if(master_thread()) {
527  // Sum up all threads statistics
528  for(i = 0; i < n_cores; i++) {
529  system_wide_stats.vec += thread_stats[i].vec;
530  }
531  system_wide_stats.exponential_event_time /= n_cores;
532  system_wide_stats.max_resident_set = getPeakRSS();
533  // GVT computations are the same for all threads
534  system_wide_stats.gvt_computations /= n_cores;
535 
536  // Compute derived statistics and dump everything
537  f = unique_files[STAT_FILE_U_NODE];
538  print_config_to_file(f);
539  fprintf(f, "\n");
540  print_header(f, "NODE STATISTICS");
541  print_timer_stats(f, &simulation_timer, &simulation_finished, total_time);
542  print_common_stats(f, &system_wide_stats, false, true);
543  print_termination_status(f, exit_code);
544  fflush(f);
545 
546  #ifdef HAVE_MPI
547  mpi_reduce_statistics(&global_stats, &system_wide_stats);
548  if(master_kernel() && n_ker > 1){
549  global_stats.exponential_event_time /= n_ker;
550  // GVT computations are the same for all kernels
551  global_stats.gvt_computations /= n_ker;
552  global_stats.simtime_advancement /= n_ker;
553 
554  f = unique_files[STAT_FILE_U_GLOBAL];
555  print_config_to_file(f);
556  fprintf(f, "\n");
557  print_header(f, "GLOBAL STATISTICS");
558  print_timer_stats(f, &simulation_timer, &simulation_finished, total_time);
559  print_common_stats(f, &global_stats, false, false);
560  print_termination_status(f, exit_code);
561  fflush(f);
562  }
563  #endif
564  if(master_kernel())
565  print_termination_status(stdout, exit_code);
566  }
567  }
568 }
569 
570 
571 // Sum up all that happened in the last GVT phase, in case it is required,
572 // dump a line on the corresponding statistics file
573 inline void statistics_on_gvt(double gvt)
574 {
575  unsigned int lid;
576  unsigned int committed = 0;
577  static __thread unsigned int cumulated = 0;
578  double exec_time, simtime_advancement, keep_exponential_event_time;
579 
580  // Dump on file only if required
582  exec_time = timer_value_seconds(simulation_timer);
583 
584  // Reduce the committed events from all LPs
585  foreach_bound_lp(lp) {
586  committed += lp_stats_gvt[lp->lid.to_int].committed_events;
587  }
588  cumulated += committed;
589 
590  // fill the row
591  gvt_buf.rows[gvt_buf.pos++] = (struct _gvt_buffer_row_t){exec_time, gvt, committed, cumulated};
592  // check if buffer is full
593  if(gvt_buf.pos >= GVT_BUFF_ROWS){
594  // flush our buffer in the blob file
595  fwrite(gvt_buf.rows, sizeof(struct _gvt_buffer_row_t), gvt_buf.pos, thread_blob_files[local_tid]);
596  gvt_buf.pos = 0;
597  }
598  }
599 
600  thread_stats[local_tid].memory_usage += (double)getCurrentRSS();
601  thread_stats[local_tid].gvt_computations += 1.0;
602 
603  simtime_advancement = gvt - thread_stats[local_tid].gvt_time;
604  if(D_DIFFER_ZERO(thread_stats[local_tid].simtime_advancement)) {
605  // Exponential moving average
606  thread_stats[local_tid].simtime_advancement =
607  0.1 * simtime_advancement +
608  0.9 * thread_stats[local_tid].simtime_advancement;
609  } else {
610  thread_stats[local_tid].simtime_advancement = simtime_advancement;
611  }
612  thread_stats[local_tid].gvt_time = gvt;
613 
614  foreach_bound_lp(lp) {
615  lid = lp->lid.to_int;
616 
617  lp_stats[lid].vec += lp_stats_gvt[lid].vec;
618 
619  lp_stats[lid].exponential_event_time = lp_stats_gvt[lid].exponential_event_time;
620 
621  keep_exponential_event_time = lp_stats_gvt[lid].exponential_event_time;
622  bzero(&lp_stats_gvt[lid], sizeof(struct stat_t));
623  lp_stats_gvt[lid].exponential_event_time = keep_exponential_event_time;
624  }
625 }
626 
627 
628 inline void statistics_on_gvt_serial(double gvt)
629 {
630  system_wide_stats.gvt_computations += 1.0;
631  system_wide_stats.memory_usage += (double)getCurrentRSS();
632 
633  double simtime_advancement = gvt - system_wide_stats.gvt_time;
634  if(D_DIFFER_ZERO(system_wide_stats.simtime_advancement)) {
635  // Exponential moving average
636  system_wide_stats.simtime_advancement =
637  0.1 * simtime_advancement +
638  0.9 * system_wide_stats.simtime_advancement;
639  } else {
640  system_wide_stats.simtime_advancement = simtime_advancement;
641  }
642 
643  system_wide_stats.gvt_time = gvt;
644 }
645 
646 
647 #define assign_new_file(destination, format, ...) \
648  do {\
649  safe_asprintf(&name_buf, "%s/" format, rootsim_config.output_dir, ##__VA_ARGS__);\
650  if (((destination) = fopen(name_buf, "w+")) == NULL)\
651  rootsim_error(true, "Cannot open %s\n", name_buf);\
652  rsfree(name_buf);\
653  } while(0)
654 
660 void statistics_init(void)
661 {
662  unsigned int i;
663  char *name_buf = NULL;
664 
665  // Make output dir if non existant
667 
668  // The whole reduction for the sequential simulation is simply done at the end
670  assign_new_file(unique_files[STAT_FILE_U_GLOBAL], "sequential_stats");
671  return;
672  }
673 
674  for(i = 0; i < n_cores; i++) {
675  safe_asprintf(&name_buf, "%s/thread_%u_%u/", rootsim_config.output_dir, kid, i);
676  _mkdir(name_buf);
677  rsfree(name_buf);
678  }
679  // create the unique file
680 #ifdef HAVE_MPI
681  if(n_ker > 1) {
682  assign_new_file(unique_files[STAT_FILE_U_NODE], STAT_FILE_NAME_NODE"_%u", kid);
683  if(master_kernel())
684  assign_new_file(unique_files[STAT_FILE_U_GLOBAL], STAT_FILE_NAME_GLOBAL);
685  } else
686 #endif
687  {
688  assign_new_file(unique_files[STAT_FILE_U_NODE], STAT_FILE_NAME_NODE);
689  }
690  // Create files depending on the actual level of verbosity
691  switch(rootsim_config.stats) {
692  case STATS_ALL:
693  case STATS_LP:
694  thread_files[STAT_FILE_T_LP] = rsalloc(sizeof(FILE *) * n_cores);
695  for(i = 0; i < n_cores; ++i) {
696  assign_new_file(thread_files[STAT_FILE_T_LP][i], "thread_%u_%u/%s", kid, i, STAT_FILE_NAME_LP);
697  }
698  if(rootsim_config.stats == STATS_LP) goto stats_lp_jump;
699  /* fall through */
700  case STATS_PERF:
701  thread_files[STAT_FILE_T_GVT] = rsalloc(sizeof(FILE *) * n_cores);
702  thread_blob_files = rsalloc(sizeof(FILE *) * n_cores);
703  for(i = 0; i < n_cores; ++i) {
704  assign_new_file(thread_files[STAT_FILE_T_GVT][i], "thread_%u_%u/%s", kid, i, STAT_FILE_NAME_GVT);
705  assign_new_file(thread_blob_files[i], "thread_%u_%u/.%s_blob", kid, i, STAT_FILE_NAME_GVT);
706  }
707  /* fall through */
708  case STATS_GLOBAL:
709  stats_lp_jump:
710  thread_files[STAT_FILE_T_THREAD] = rsalloc(sizeof(FILE *) * n_cores);
711  for(i = 0; i < n_cores; ++i) {
712  assign_new_file(thread_files[STAT_FILE_T_THREAD][i], "thread_%u_%u/%s", kid, i, STAT_FILE_NAME_THREAD);
713  }
714  break;
715  default:
716  rootsim_error(true, "unrecognized statistics option '%d'!", rootsim_config.stats);
717  }
718 
719  // Initialize data structures to keep information
720  lp_stats = rsalloc(n_prc * sizeof(struct stat_t));
721  bzero(lp_stats, n_prc * sizeof(struct stat_t));
722  lp_stats_gvt = rsalloc(n_prc * sizeof(struct stat_t));
723  bzero(lp_stats_gvt, n_prc * sizeof(struct stat_t));
724  thread_stats = rsalloc(n_cores * sizeof(struct stat_t));
725  bzero(thread_stats, n_cores * sizeof(struct stat_t));
726 }
727 
728 #undef assign_new_file
729 
730 
736 void statistics_fini(void)
737 {
738  register unsigned int i, j;
739 
740  for(i = 0; i < NUM_STAT_FILE_U; i++) {
741  if(unique_files[i] != NULL)
742  fclose(unique_files[i]);
743  }
744 
745  for(i = 0; i < NUM_STAT_FILE_T; i++) {
746  if(!thread_files[i]) continue;
747  for(j = 0; j < n_cores; j++) {
748  if(thread_files[i][j] != NULL)
749  fclose(thread_files[i][j]);
750  }
751  rsfree(thread_files[i]);
752  }
753 
754  rsfree(thread_stats);
755  rsfree(lp_stats);
756  rsfree(lp_stats_gvt);
757 }
758 
759 
760 void statistics_post_data_serial(enum stat_msg_t type, double data)
761 {
762  switch(type) {
763  case STAT_EVENT:
764  system_wide_stats.tot_events += 1.0;
765  break;
766 
767  case STAT_EVENT_TIME:
768  system_wide_stats.event_time += data;
769  system_wide_stats.exponential_event_time = 0.1 * data + 0.9 * system_wide_stats.exponential_event_time;
770  break;
771 
772  default:
773  rootsim_error(true, "Wrong LP statistics post type: %d. Aborting...\n", type);
774  }
775 }
776 
777 
778 void statistics_post_data(struct lp_struct *lp, enum stat_msg_t type, double data)
779 {
780  // TODO: this is only required to avoid a nasty segfault if we
781  // pass NULL to lp, as we do for the case of STAT_IDLE_CYCLES.
782  // We should move stats arrays in struct lp_struct to make the
783  // whole code less ugly.
784  unsigned int lid = lp ? lp->lid.to_int : UINT_MAX;
785 
786  switch(type) {
787 
788  case STAT_ANTIMESSAGE:
789  lp_stats_gvt[lid].tot_antimessages += 1.0;
790  break;
791 
792  case STAT_EVENT:
793  lp_stats_gvt[lid].tot_events += 1.0;
794  break;
795 
796  case STAT_EVENT_TIME:
797  lp_stats_gvt[lid].event_time += data;
798  lp_stats_gvt[lid].exponential_event_time = 0.1 * data + 0.9 * lp_stats_gvt[lid].exponential_event_time;
799  break;
800 
801  case STAT_COMMITTED:
802  lp_stats_gvt[lid].committed_events += data;
803  break;
804 
805  case STAT_ROLLBACK:
806  lp_stats_gvt[lid].tot_rollbacks += 1.0;
807  break;
808 
809  case STAT_CKPT:
810  lp_stats_gvt[lid].tot_ckpts += 1.0;
811  break;
812 
813  case STAT_CKPT_MEM:
814  lp_stats_gvt[lid].ckpt_mem += data;
815  break;
816 
817  case STAT_CKPT_TIME:
818  lp_stats_gvt[lid].ckpt_time += data;
819  break;
820 
821  case STAT_RECOVERY:
822  lp_stats_gvt[lid].tot_recoveries++;
823  break;
824 
825  case STAT_RECOVERY_TIME:
826  lp_stats_gvt[lid].recovery_time += data;
827  break;
828 
829  case STAT_IDLE_CYCLES:
830  thread_stats[local_tid].idle_cycles++;
831  break;
832 
833  case STAT_SILENT:
834  lp_stats_gvt[lid].reprocessed_events += data;
835  break;
836 
837  case STAT_GVT_ROUND_TIME:
838  system_wide_stats.gvt_round_time_min = fmin(data, system_wide_stats.gvt_round_time_min);
839  system_wide_stats.gvt_round_time_max = fmax(data, system_wide_stats.gvt_round_time_max);
840  system_wide_stats.gvt_round_time += data;
841  break;
842 
843  default:
844  rootsim_error(true, "Wrong LP statistics post type: %d. Aborting...\n", type);
845  }
846 }
847 
848 
849 double statistics_get_lp_data(struct lp_struct *lp, unsigned int type)
850 {
851  switch(type) {
852 
853  case STAT_GET_EVENT_TIME_LP:
854  return lp_stats[lp->lid.to_int].exponential_event_time;
855 
856  default:
857  rootsim_error(true, "Wrong statistics get type: %d. Aborting...\n", type);
858  }
859  return 0.0;
860 }
static struct stat_t * thread_stats
Keeps statistics on a per-thread basis.
Definition: statistics.c:113
static struct stat_t * lp_stats_gvt
Keeps statistics on a per-LP basis in a GVT phase.
Definition: statistics.c:110
void statistics_fini(void)
Definition: statistics.c:736
void statistics_start(void)
Definition: statistics.c:286
Initialization routines.
Message queueing subsystem.
seed_type set_seed
The master seed to be used in this run.
Definition: init.h:73
static void _rmdir(const char *path)
Definition: statistics.c:154
static struct stat_t system_wide_stats
Keeps global statistics.
Definition: statistics.c:116
int check_termination_mode
Check termination strategy: standard or incremental.
Definition: init.h:68
static char * format_size(double size)
Definition: statistics.c:298
char * output_dir
Destination Directory of output files.
Definition: init.h:58
Core ROOT-Sim functionalities.
#define safe_asprintf(ret_addr, format,...)
Definition: statistics.c:136
void statistics_init(void)
Definition: statistics.c:660
unsigned int n_cores
Total number of cores required for simulation.
Definition: core.c:61
int ckpt_period
Number of events to execute before taking a snapshot in PSS (ignored otherwise)
Definition: init.h:66
void _mkdir(const char *path)
Definition: statistics.c:198
simtime_t get_last_gvt(void)
Definition: gvt.c:179
#define timer_tostring(timer_name, string)
string must be a char array of at least TIMER_BUFFER_LEN bytes to keep the whole string ...
Definition: timer.h:64
int checkpointing
Type of checkpointing scheme (e.g., PSS, CSS, ...)
Definition: init.h:65
unsigned int to_int
The LID numerical value.
Definition: core.h:145
static FILE ** thread_blob_files
Pointers to the files used as binary buffer for the GVT statistics.
Definition: statistics.c:97
Timers.
Statistics module.
The ROOT-Sim scheduler main module header.
size_t getCurrentRSS(void)
Definition: memusage.c:119
Generic thread management facilities.
static timer simulation_timer
This is a timer that start during the initialization of statistics subsystem and can be used to know ...
Definition: statistics.c:104
bool mpi_support_multithread
Flag telling whether the MPI runtime supports multithreading.
Definition: mpi.c:48
static struct stat_t * lp_stats
Keeps statistics on a per-LP basis.
Definition: statistics.c:107
void mpi_reduce_statistics(struct stat_t *global, struct stat_t *local)
Invoke statistics reduction.
Definition: mpi.c:428
#define INFTY
Infinite timestamp: this is the highest timestamp in a simulation run.
Definition: ROOT-Sim.h:58
#define D_DIFFER_ZERO(a)
Difference from zero condition for doubles.
Definition: core.h:100
int scheduler
Which scheduler to be used.
Definition: init.h:59
simulation_configuration rootsim_config
This global variable holds the configuration for the current simulation.
Definition: core.c:70
Memory Manager main header.
Memory usage module header.
static FILE ** thread_files[NUM_STAT_FILE_T]
Pointers to per-thread files.
Definition: statistics.c:101
static FILE * unique_files[NUM_STAT_FILE_U]
Pointers to unique files.
Definition: statistics.c:99
bool thread_barrier(barrier_t *b)
Definition: thread.c:200
int simulation_time
Wall-clock-time based termination predicate.
Definition: init.h:62
LP control blocks.
barrier_t all_thread_barrier
Barrier for all worker threads.
Definition: core.c:49
#define get_cores()
Macro to get the core count on the hosting machine.
Definition: thread.h:48
#define master_thread()
This macro expands to true if the current KLT is the master thread for the local kernel.
Definition: thread.h:155
MPI Support Module.
LP state management.
unsigned int n_prc
Number of logical processes hosted by the current kernel instance.
Definition: core.c:67
#define master_kernel()
This macro expands to true if the local kernel is the master kernel.
Definition: core.h:47
const char *const param_to_text[][5]
Definition: init.c:101
Global Virtual Time.
size_t getPeakRSS(void)
Definition: memusage.c:70
enum stats_levels stats
Produce performance statistic file (default STATS_ALL)
Definition: init.h:71
LID_t lid
Local ID of the LP.
Definition: process.h:79
int lps_distribution
Policy for the LP to Kernel mapping.
Definition: init.h:63
__thread unsigned int n_prc_per_thread
This is used to keep track of how many LPs were bound to the current KLT.
Definition: scheduler.c:69
void statistics_stop(int exit_code)
Definition: statistics.c:442
#define MAX_PATHLEN
Longest length of a path.
Definition: statistics.h:62
__thread unsigned int local_tid
Definition: thread.c:72
int snapshot
Type of snapshot (e.g., full, incremental, autonomic, ...)
Definition: init.h:67
static __thread struct _gvt_buffer gvt_buf
This structure is used to buffer statistics gathered on GVT computation.
Definition: statistics.c:94
unsigned int n_prc_tot
Total number of logical processes running in the simulation.
Definition: core.c:64
int gvt_time_period
Wall-Clock time to wait before executiong GVT operations.
Definition: init.h:60
bool serial
If the simulation must be run serially.
Definition: init.h:72
unsigned int n_ker
Total number of simulation kernel instances running.
Definition: core.c:58
unsigned int kid
Identifier of the local kernel.
Definition: core.c:55