The ROme OpTimistic Simulator  3.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
stats.c
Go to the documentation of this file.
1 
12 #include <log/stats.h>
13 
14 #include <arch/io.h>
15 #include <arch/mem.h>
16 #include <arch/timer.h>
17 #include <core/arg_parse.h>
18 #include <core/core.h>
19 
20 #include <assert.h>
21 #include <inttypes.h>
22 #include <memory.h>
23 #include <stdarg.h>
24 #include <stdatomic.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 
28 #define STATS_BUFFER_ENTRIES (1024)
29 #define STD_DEV_POWER_2_EXP 5
30 #define STATS_MAX_STRLEN 32U
31 
33 
34 struct stats_measure {
36  uint64_t count;
38  uint64_t sum_t;
40  uint64_t var_t;
41 };
42 
44 struct stats_thread {
46  uint64_t rt;
48  struct stats_measure s[STATS_COUNT];
49 };
50 
51 struct stats_node {
55  uint64_t rss;
56 };
57 
58 struct stats_glob {
60  uint64_t threads_count;
62  uint64_t max_rss;
64  uint64_t glob_init_rt;
66  uint64_t glob_fini_rt;
68  uint64_t timestamps[STATS_GLOBAL_COUNT];
69 };
70 
71 static_assert(sizeof(struct stats_measure) == 24 &&
72  sizeof(struct stats_thread) == 8 + 24 * STATS_COUNT &&
73  sizeof(struct stats_node) == 16 &&
74  sizeof(struct stats_glob) == 32 + 8 * (STATS_GLOBAL_COUNT),
75  "structs aren't naturally packed, parsing may be difficult");
76 
78 const char * const s_names[] = {
79  [STATS_ROLLBACK] = "rollbacks",
80  [STATS_GVT] = "gvt",
81  [STATS_MSG_SILENT] = "silent messages",
82  [STATS_MSG_PROCESSED] = "processed messages"
83 };
84 
85 static timer_uint sim_start_ts;
86 static struct stats_glob stats_glob_cur;
87 
88 static FILE *stats_node_tmp;
89 static FILE **stats_tmps;
90 static __thread struct stats_thread stats_cur;
91 
92 static __thread timer_uint last_ts[STATS_COUNT];
93 
94 static void file_write_chunk(FILE *f, const void *data, size_t data_size)
95 {
96  if (unlikely(fwrite(data, data_size, 1, f) != 1))
97  log_log(LOG_ERROR, "Error during disk write!");
98 }
99 
100 static void *file_memory_load(FILE *f, int64_t *f_size_p)
101 {
102  fseek(f, 0, SEEK_END);
103  long f_size = ftell(f); // Fails horribly for files bigger than 2 GB
104  fseek(f, 0, SEEK_SET);
105  void *ret = mm_alloc(f_size);
106  if (fread(ret, f_size, 1, f) != 1) {
107  mm_free(ret);
108  *f_size_p = 0;
109  return NULL;
110  }
111  *f_size_p = f_size;
112  return ret;
113 }
114 
121 static FILE *file_open(const char *open_type, const char *fmt, ...)
122 {
123  va_list args, args_cp;
124  va_start(args, fmt);
125  va_copy(args_cp, args);
126 
127  size_t l = vsnprintf(NULL, 0, fmt, args_cp) + 1;
128  va_end(args_cp);
129 
130  char *f_name = mm_alloc(l);
131  vsnprintf(f_name, l, fmt, args);
132  va_end(args);
133 
134  FILE *ret = fopen(f_name, open_type);
135  if (ret == NULL)
136  log_log(LOG_ERROR, "Unable to open \"%s\" in %s mode", f_name,
137  open_type);
138 
139  mm_free(f_name);
140  return ret;
141 }
142 
147 {
148  sim_start_ts = timer_new();
149 }
150 
154 void stats_global_time_take(enum stats_global_time this_stat)
155 {
156  stats_glob_cur.timestamps[this_stat] = timer_value(sim_start_ts);
157 }
158 
163 {
164  stats_glob_cur.glob_init_rt = timer_value(sim_start_ts);
165  stats_glob_cur.threads_count = n_threads;
166  if (mem_stat_setup() < 0)
167  log_log(LOG_ERROR, "Unable to extract memory statistics!");
168 
169  stats_node_tmp = io_file_tmp_get();
170  setvbuf(stats_node_tmp, NULL, _IOFBF,
171  STATS_BUFFER_ENTRIES * sizeof(struct stats_node));
172 
173  stats_tmps = mm_alloc(n_threads * sizeof(*stats_tmps));
174 }
175 
179 void stats_init(void)
180 {
181  stats_tmps[rid] = io_file_tmp_get();
182  setvbuf(stats_tmps[rid], NULL, _IOFBF,
183  STATS_BUFFER_ENTRIES * sizeof(stats_cur));
184 }
185 
186 #ifdef ROOTSIM_MPI
187 
188 static void stats_files_receive(FILE *o)
189 {
190  for (nid_t j = 1; j < n_nodes; ++j) {
191  int buf_size;
192  struct stats_glob *sg_p = mpi_blocking_data_rcv(&buf_size, j);
193  file_write_chunk(o, sg_p, buf_size);
194  uint64_t iters = sg_p->threads_count + 1; // +1 for node stats
195  mm_free(sg_p);
196 
197  for (uint64_t i = 0; i < iters; ++i) {
198  void *buf = mpi_blocking_data_rcv(&buf_size, j);
199  int64_t f_size = buf_size;
200  file_write_chunk(o, &f_size, sizeof(f_size));
201  file_write_chunk(o, buf, buf_size);
202  mm_free(buf);
203  }
204  }
205 }
206 
207 static void stats_files_send(void)
208 {
209  stats_glob_cur.max_rss = mem_stat_rss_max_get();
210  stats_glob_cur.glob_fini_rt = timer_value(sim_start_ts);
211  mpi_blocking_data_send(&stats_glob_cur, sizeof(stats_glob_cur), 0);
212 
213  int64_t f_size;
214  void *f_buf = file_memory_load(stats_node_tmp, &f_size);
215  f_size = min(INT_MAX, f_size);
216  mpi_blocking_data_send(f_buf, f_size, 0);
217  mm_free(f_buf);
218 
219  for (rid_t i = 0; i < n_threads; ++i) {
220  f_buf = file_memory_load(stats_tmps[i], &f_size);
221  f_size = min(INT_MAX, f_size);
222  mpi_blocking_data_send(f_buf, f_size, 0);
223  mm_free(f_buf);
224  }
225 }
226 
227 #endif
228 
229 static void stats_file_final_write(FILE *o)
230 {
231  uint16_t endian_check = 61455U; // 0xFOOF
232  file_write_chunk(o, &endian_check, sizeof(endian_check));
233 
234  int64_t n = STATS_COUNT;
235  file_write_chunk(o, &n, sizeof(n));
236  for (int i = 0; i < STATS_COUNT; ++i) {
237  size_t l = min(strlen(s_names[i]), STATS_MAX_STRLEN);
238  file_write_chunk(o, s_names[i], l);
239  unsigned char nul = 0;
240  for (; l < STATS_MAX_STRLEN; ++l)
241  file_write_chunk(o, &nul, 1);
242  }
243 
244  n = n_nodes;
245  file_write_chunk(o, &n, sizeof(n));
246 
247  stats_glob_cur.max_rss = mem_stat_rss_max_get();
248  stats_glob_cur.glob_fini_rt = timer_value(sim_start_ts);
249  file_write_chunk(o, &stats_glob_cur, sizeof(stats_glob_cur));
250 
251  int64_t buf_size;
252  void *buf = file_memory_load(stats_node_tmp, &buf_size);
253  file_write_chunk(o, &buf_size, sizeof(buf_size));
254  file_write_chunk(o, buf, buf_size);
255  mm_free(buf);
256 
257  for (rid_t i = 0; i < n_threads; ++i) {
258  buf = file_memory_load(stats_tmps[i], &buf_size);
259  file_write_chunk(o, &buf_size, sizeof(buf_size));
260  file_write_chunk(o, buf, buf_size);
261  mm_free(buf);
262  }
263 }
264 
274 {
275 #ifdef ROOTSIM_MPI
277  if (nid) {
278  stats_files_send();
279  return;
280  }
281 #endif
282  FILE *o = file_open("w", "%s_stats.bin", arg_parse_program_name());
283  if (o == NULL) {
284  log_log(LOG_WARN, "Unavailable stats file: stats will be dumped on stdout");
285  o = stdout;
286  }
287 
288  stats_file_final_write(o);
289 
290 #ifdef ROOTSIM_MPI
291  stats_files_receive(o);
292 #endif
293 
294  fflush(o);
295  if (o != stdout)
296  fclose(o);
297 }
298 
299 void stats_time_start(enum stats_time this_stat)
300 {
301  last_ts[this_stat] = timer_new();
302 }
303 
304 void stats_time_take(enum stats_time this_stat)
305 {
306  struct stats_measure *s_mes = &stats_cur.s[this_stat];
307  const uint64_t t = timer_value(last_ts[this_stat]);
308 
309  if (likely(s_mes->count)) {
310  const int64_t num = (t * s_mes->count - s_mes->sum_t);
311  s_mes->var_t += ((num * num) << (2 * STD_DEV_POWER_2_EXP)) /
312  (s_mes->count * (s_mes->count + 1));
313  }
314 
315  s_mes->sum_t += t;
316  s_mes->count++;
317 }
318 
319 void stats_on_gvt(simtime_t gvt)
320 {
321  stats_cur.rt = timer_value(sim_start_ts);
322 
323  file_write_chunk(stats_tmps[rid], &stats_cur, sizeof(stats_cur));
324  memset(&stats_cur, 0, sizeof(stats_cur));
325 
326  if (rid != 0)
327  return;
328 
329  struct stats_node stats_node_cur =
330  {.gvt = gvt, .rss = mem_stat_rss_current_get()};
331  file_write_chunk(stats_node_tmp, &stats_node_cur, sizeof(stats_node_cur));
332  memset(&stats_node_cur, 0, sizeof(stats_node_cur));
333 
334  printf("\rVirtual time: %lf", gvt);
335  fflush(stdout);
336 }
337 
338 void stats_dump(void)
339 {
340  puts("");
341  fflush(stdout);
342 }
stats_init
void stats_init(void)
Initializes the stats subsystem in the current thread.
Definition: stats.c:179
simtime_t
double simtime_t
The type used to represent logical time in the simulation.
Definition: core.h:62
rid_t
unsigned rid_t
Used to identify in a node the computing resources (threads at the moment)
Definition: core.h:77
LOG_ERROR
#define LOG_ERROR
The logging level reserved to unexpected, problematic conditions.
Definition: log.h:33
nid
nid_t nid
The node identifier of the node.
Definition: core.c:20
io_file_tmp_get
FILE * io_file_tmp_get(void)
Creates a temporary file.
nid_t
int nid_t
Used to identify MPI nodes in a distributed environment.
Definition: core.h:79
stats_glob::glob_fini_rt
uint64_t glob_fini_rt
The latest ts before having to aggregate stats.
Definition: stats.c:66
stats_thread
A container for statistics in a logical time period.
Definition: stats.c:44
stats_node
Definition: stats.c:51
mm_alloc
void * mm_alloc(size_t mem_size)
A version of the stdlib malloc() used internally.
Definition: mm.h:26
stats_global_fini
void stats_global_fini(void)
Finalizes the stats subsystem in the node.
Definition: stats.c:273
mpi_node_barrier
void mpi_node_barrier(void)
A node barrier.
Definition: mpi.c:365
n_threads
rid_t n_threads
The total number of MPI nodes in the simulation.
Definition: core.c:15
mpi_blocking_data_rcv
void * mpi_blocking_data_rcv(int *data_size_p, nid_t src)
Receives a byte buffer from another node.
Definition: mpi.c:397
stats_global_time_start
void stats_global_time_start(void)
Initializes the internal timer used to take accurate measurements.
Definition: stats.c:146
timer_uint
uint_fast64_t timer_uint
Definition: timer.h:20
stats_measure::count
uint64_t count
The count of events of this type.
Definition: stats.c:36
stats.h
Statistics module.
log_log
#define log_log(lvl,...)
Produces a log.
Definition: log.h:49
timer.h
Timers.
stats_glob::threads_count
uint64_t threads_count
The number of threads in this node.
Definition: stats.c:60
stats_glob
Definition: stats.c:58
rid
__thread rid_t rid
The identifier of the thread.
Definition: core.c:16
mm_free
void mm_free(void *ptr)
A version of the stdlib free() used internally.
Definition: mm.h:61
stats_measure::var_t
uint64_t var_t
The variance of the time to complete multiplied by the events count.
Definition: stats.c:40
likely
#define likely(exp)
Optimize the branch as likely taken.
Definition: core.h:57
s_names
const char *const s_names[]
The statistics names, used to fill in the header of the final csv.
Definition: stats.c:78
LOG_WARN
#define LOG_WARN
The logging level reserved to unexpected, non deal breaking conditions.
Definition: log.h:31
stats_glob::glob_init_rt
uint64_t glob_init_rt
When stats global init has been called.
Definition: stats.c:64
io.h
Generic input-output facilities.
arg_parse.h
Command line option parser.
stats_node::rss
uint64_t rss
The size in bytes of the resident set.
Definition: stats.c:55
core.h
Core ROOT-Sim functionalities.
unlikely
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:59
stats_node::gvt
simtime_t gvt
The gvt value.
Definition: stats.c:53
stats_global_init
void stats_global_init(void)
Initializes the stats subsystem in the node.
Definition: stats.c:162
timer_value
timer_uint timer_value(timer_uint start)
Computes a time interval measure using a previous timer_uint value.
mpi_blocking_data_send
void mpi_blocking_data_send(const void *data, int data_size, nid_t dest)
Sends a byte buffer to another node.
Definition: mpi.c:381
mem.h
Platform specific memory utilities.
stats_measure::sum_t
uint64_t sum_t
The mean time to complete an event multiplied by the events count.
Definition: stats.c:38
stats_thread::s
struct stats_measure s[STATS_COUNT]
The array of statistics taken in the period.
Definition: stats.c:48
stats_global_time_take
void stats_global_time_take(enum stats_global_time this_stat)
Initializes the internal timer used to take accurate measurements.
Definition: stats.c:154
arg_parse_program_name
const char * arg_parse_program_name(void)
Gets the program name.
Definition: arg_parse.c:422
stats_thread::rt
uint64_t rt
Real elapsed time in microseconds from simulation beginning.
Definition: stats.c:46
stats_glob::max_rss
uint64_t max_rss
The maximum size in bytes of the resident set.
Definition: stats.c:62
timer_new
timer_uint timer_new(void)
Gets a new starting point for an time interval measure.
stats_glob::timestamps
uint64_t timestamps[STATS_GLOBAL_COUNT]
The timestamps of the relevant simulation life-cycle events.
Definition: stats.c:68
stats_measure
A set of statistical values of a single metric.
Definition: stats.c:34
file_open
static FILE * file_open(const char *open_type, const char *fmt,...)
A version of fopen() which accepts a printf style format string.
Definition: stats.c:121