MACSio  0.9
Multi-purpose, Application-Centric, Scalable I/O Proxy App
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
macsio_timing.c
Go to the documentation of this file.
1 /*
2 Copyright (c) 2015, Lawrence Livermore National Security, LLC.
3 Produced at the Lawrence Livermore National Laboratory.
4 Written by Mark C. Miller
5 
6 LLNL-CODE-676051. All rights reserved.
7 
8 This file is part of MACSio
9 
10 Please also read the LICENSE file at the top of the source code directory or
11 folder hierarchy.
12 
13 This program is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License (as published by the Free Software
15 Foundation) version 2, dated June 1991.
16 
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU General
20 Public License for more details.
21 
22 You should have received a copy of the GNU General Public License along with
23 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26 
27 #include <macsio_timing.h>
28 #include <macsio_utils.h>
29 
30 #ifdef HAVE_MPI
31 #include <mpi.h>
32 #endif
33 
34 #include <cfloat>
35 #include <climits>
36 #include <math.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/time.h>
41 
42 #define MACSIO_TIMING_HASH_TABLE_SIZE 10007
43 
45 
46 static double get_current_time()
47 {
48  static int first = 1;
49 
50 #ifdef HAVE_MPI
52  {
53  static double t0;
54  if (first)
55  {
56  first = 0;
57  t0 = MPI_Wtime();
58  return 0;
59  }
60  return MPI_Wtime() - t0;
61  }
62 #endif
63 
64 #if defined(_WIN32)
65 
66  static struct _timeb T0;
67  struct _timeb T1;
68  int ms;
69 
70  /* initilize T0 */
71  if (first)
72  {
73  first = 0;
74  _ftime(&T0);
75  return 0.0;
76  }
77 
78  _ftime(T1);
79 
80  ms = (int) difftime(T1.time, T0.time);
81 
82  if (ms == 0)
83  {
84  ms = T1.millitm - T0.millitm;
85  }
86  else
87  {
88  ms = ((ms - 1) * 1000);
89  ms += (1000 - T0.millitm) + T1.millitm;
90  }
91 
92  return (ms/1000.);
93 
94 #else
95 
96  static struct timeval T0;
97  struct timeval T1;
98 
99  if (first)
100  {
101  first = 0;
102  gettimeofday(&T0, 0);
103  return 0.0;
104  }
105 
106  gettimeofday(&T1, 0);
107 
108  return (double) (T1.tv_sec - T0.tv_sec) +
109  (double) (T1.tv_usec - T0.tv_usec) / 1000000.;
110 
111 #endif
112 }
113 
114 /* A small collection of strings to be associated with different
115  timer groups. That is, collections of timers that are used to
116  time different phases of some larger class of activity. For
117  example for MIF-I/O, there are various activities that we may
118  time including initial creates, handoffs (close->open) as well
119  as each processor's work on the file. */
120 static int const maxTimerGroups = sizeof(MACSIO_TIMING_GroupMask_t)*8;
122 static int timerGroupCount = 0;
124 group_mask_from_name(char const *grpName)
125 {
126  int i;
127 
128  if (grpName == 0)
129  {
130  if (timerGroupCount == 0)
131  {
132  for (i = 0; i < maxTimerGroups; i++)
133  timerGroupNames[i] = 0;
134  }
135  else
136  {
137  for (i = 0; i < maxTimerGroups; i++)
138  {
139  if (timerGroupNames[i])
140  free(timerGroupNames[i]);
141  timerGroupNames[i] = 0;
142  }
143  timerGroupCount = 0;
144  }
145  return 0;
146  }
147 
148  for (i = 0; i < maxTimerGroups; i++)
149  {
150  if (timerGroupNames[i] && !strcmp(timerGroupNames[i], grpName))
151  return ((MACSIO_TIMING_GroupMask_t)1)<<i;
152  }
153 
154  if (timerGroupCount == maxTimerGroups)
155  return 0;
156 
157  timerGroupNames[timerGroupCount] = strdup(grpName);
158  timerGroupCount++;
160 }
161 
163 MACSIO_TIMING_GroupMask(char const *grpName)
164 {
165  return group_mask_from_name(grpName);
166 }
167 
168 typedef struct _timerInfo_t
169 {
170  /* If you change this structure in any way, you need to change the code that
171  creates its MPI datatype in ReduceTimers */
172  int __line__;
174  int min_iter;
175  int max_iter;
176  int min_rank;
177  int max_rank;
178  int iter_num;
179  int depth;
182  double total_time;
183  double min_time;
184  double max_time;
185  double running_mean;
186  double running_var;
187  double start_time;
192  char __file__[32];
193  char label[64];
195 } timerInfo_t;
196 
198 #ifdef HAVE_MPI
200 #endif
201 
203  char const *label,
205  int iter_num,
206  char const *__file__,
207  int __line__
208 )
209 {
210  int n = 0;
211  int len = strlen(__file__) + strlen(label) + 64;
212  char* _label = (char *) malloc(len);
213  int len2 = snprintf(_label, len, "%s:%05d:%016llX:%s", __file__, __line__, gmask, label);
214  MACSIO_TIMING_TimerId_t tid = MACSIO_UTILS_BJHash((unsigned char*)_label, len2, 0) % MACSIO_TIMING_HASH_TABLE_SIZE;
215  int inc = (tid > MACSIO_TIMING_HASH_TABLE_SIZE / 2) ? -1 : 1;
216 
217  free(_label);
218 
219  /* Find the timer's slot in the hash table */
221  {
222  if (!strlen(timerHashTable[tid].label))
223  {
224  /* Starting a new timer for the first time/iteration */
225  strncpy(timerHashTable[tid].__file__, __file__, sizeof(timerHashTable[tid].__file__));
226  timerHashTable[tid].__line__ = __line__;
227  strncpy(timerHashTable[tid].label, label, sizeof(timerHashTable[tid].label));
228  timerHashTable[tid].gmask = gmask;
229 
230  timerHashTable[tid].total_time = 0;
231  timerHashTable[tid].iter_count = 0;
232  timerHashTable[tid].min_time = DBL_MAX;
233  timerHashTable[tid].max_time = -DBL_MAX;
234  timerHashTable[tid].min_iter = INT_MAX;
235  timerHashTable[tid].max_iter = -INT_MAX;
236  timerHashTable[tid].running_mean = 0;
237  timerHashTable[tid].running_var = 0;
238  timerHashTable[tid].iter_num = iter_num;
239  timerHashTable[tid].total_time_this_iter = 0;
240  timerHashTable[tid].is_restart = 0;
241 
242  timerHashTable[tid].depth = 0;
243  timerHashTable[tid].start_time = get_current_time();
244  return tid;
245  }
246 
247  if (strncmp(timerHashTable[tid].label, label, sizeof(timerHashTable[tid].label)) == 0 &&
248  strncmp(timerHashTable[tid].__file__, __file__, sizeof(timerHashTable[tid].__file__)) == 0 &&
249  timerHashTable[tid].__line__ == __line__)
250  {
251  /* Another iteration of or re-starting an existing timer */
252  timerHashTable[tid].is_restart = 0;
253  if (iter_num == timerHashTable[tid].iter_num)
254  timerHashTable[tid].is_restart = 1;
255  else if (iter_num == MACSIO_TIMING_ITER_AUTO)
256  timerHashTable[tid].iter_num++;
257  else
258  timerHashTable[tid].iter_num = iter_num;
259  timerHashTable[tid].start_time = get_current_time();
260  return tid;
261  }
262 
263  /* We have a collision. Try next slot in table. */
264  if (inc == 1 && tid == MACSIO_TIMING_HASH_TABLE_SIZE-1)
265  tid = 0;
266  else if (inc == -1 && tid == 0)
268  else
269  tid += inc;
270  n++;
271  }
272 
273 #warning LOG THIS ERROR
274  /* log a fatal error */
275 
277 }
278 
280 {
281  double stop_time = get_current_time();
282  double timer_time = stop_time - timerHashTable[tid].start_time;
283 
284  if (tid >= MACSIO_TIMING_HASH_TABLE_SIZE) return DBL_MAX;
285 
286  if (timerHashTable[tid].is_restart)
287  {
288  timerHashTable[tid].total_time_this_iter += timer_time;
289  timerHashTable[tid].total_time += timer_time;
290 #warning UPDATE MIN/MAX TOO
291  }
292  else
293  {
294  double var;
295  int n = timerHashTable[tid].iter_count;
296  double x = timer_time;
297  double mean = timerHashTable[tid].running_mean;
298  double M2 = timerHashTable[tid].running_var;
299  double delta = x - mean;
300 
301  /* Running or "online" algorithm (due to Knuth) for updating mean and variance
302  via recurrence relation */
303  n++;
304  mean = mean + delta / n;
305  M2 = M2 + delta * (x - mean);
306  if (n < 2)
307  var = 0;
308  else
309  var = M2/(n-1);
310 
311  /* Now, update the time record */
312  timerHashTable[tid].iter_count = n;
313  timerHashTable[tid].running_mean = mean;
314  timerHashTable[tid].running_var = var;
315  timerHashTable[tid].total_time += timer_time;
316 
317  if (timer_time < timerHashTable[tid].min_time)
318  {
319  timerHashTable[tid].min_time = timer_time;
320  timerHashTable[tid].min_iter = timerHashTable[tid].iter_num;
321  }
322  if (timer_time > timerHashTable[tid].max_time)
323  {
324  timerHashTable[tid].max_time = timer_time;
325  timerHashTable[tid].max_iter = timerHashTable[tid].iter_num;
326  }
327  }
328 
329  return timer_time;
330 }
331 
332 static double get_timer(timerInfo_t const *table, MACSIO_TIMING_TimerId_t tid, char const *field)
333 {
334  if (tid >= MACSIO_TIMING_HASH_TABLE_SIZE) return DBL_MAX;
335 
336  if (!strncmp(field, "__line__", 8))
337  return table[tid].__line__;
338  else if (!strncmp(field, "iter_count", 10))
339  return table[tid].iter_count;
340  else if (!strncmp(field, "min_iter", 8))
341  return table[tid].min_iter;
342  else if (!strncmp(field, "max_iter", 8))
343  return table[tid].max_iter;
344  else if (!strncmp(field, "min_rank", 8))
345  return table[tid].min_rank;
346  else if (!strncmp(field, "max_rank", 8))
347  return table[tid].max_rank;
348  else if (!strncmp(field, "iter_num", 8))
349  return table[tid].iter_num;
350  else if (!strncmp(field, "depth", 5))
351  return table[tid].depth;
352  else if (!strncmp(field, "total_time", 10))
353  return table[tid].total_time;
354  else if (!strncmp(field, "min_time", 8))
355  return table[tid].min_time;
356  else if (!strncmp(field, "max_time", 8))
357  return table[tid].max_time;
358  else if (!strncmp(field, "running_mean", 12))
359  return table[tid].running_mean;
360  else if (!strncmp(field, "running_var", 11))
361  return table[tid].running_var;
362 
363  return DBL_MAX;
364 }
365 
366 double MACSIO_TIMING_GetTimer(MACSIO_TIMING_TimerId_t tid, char const *field)
367 {
368  return get_timer(timerHashTable, tid, field);
369 }
370 
372 {
373  return get_timer(reducedTimerTable, tid, field);
374 }
375 
376 static void
378 {
379  int i;
380  for (i = 0; i < MACSIO_TIMING_HASH_TABLE_SIZE; i++)
381  {
382  if (!strlen(table[i].label)) continue;
383 
384  if (!(table[i].gmask & gmask)) continue;
385 
386  memset(table[i].label, 0, sizeof(table[i].label));
387  memset(table[i].__file__, 0, sizeof(table[i].__file__));
388  table[i].__line__ = 0;
389  table[i].gmask = 0;
390 
391  table[i].total_time = 0;
392  table[i].iter_count = 0;
393  table[i].min_time = DBL_MAX;
394  table[i].max_time = -DBL_MAX;
395  table[i].min_iter = INT_MAX;
396  table[i].max_iter = -INT_MAX;
397  table[i].running_mean = 0;
398  table[i].running_var = 0;
399  table[i].iter_num = 0;
400  table[i].total_time_this_iter = 0;
401 
402  table[i].is_restart = 0;
403  table[i].depth = 0;
404  table[i].start_time = 0;
405  }
406 }
407 
408 #ifdef HAVE_MPI
409 static void
411  void *a,
412  void *b,
413  int *len,
414  MPI_Datatype *type
415 )
416 {
417  int i;
418  timerInfo_t *a_info = (timerInfo_t*) a;
419  timerInfo_t *b_info = (timerInfo_t*) b;
420 
421  for (i = 0; i < *len; i++)
422  {
423  if (strlen(a_info[i].label) == 0 && strlen(b_info[i].label) == 0)
424  continue;
425 
426  /* If filenames don't match, record that fact by setting b (out) to all '~' chars */
427  if (strcmp(a_info[i].__file__, b_info[i].__file__))
428  {
429  int j = 0;
430  while (b_info[i].__file__[j])
431  b_info[i].__file__[j++] = '~';
432  }
433 
434  /* If line numbers don't match, record that fact as INT_MAX */
435  if (a_info[i].__line__ != b_info[i].__line__)
436  b_info[i].__line__ = INT_MAX;
437 
438  /* If labels don't match, record that fact by setting b (out) to all '~' chars */
439  if (strcmp(a_info[i].label, b_info[i].label))
440  {
441  int j = 0;
442  while (b_info[i].label[j])
443  b_info[i].label[j++] = '~';
444  }
445 
446  /* If groups don't match, record that fact as ALL_GROUPS */
447  if (a_info[i].gmask != b_info[i].gmask)
448  b_info[i].gmask = MACSIO_TIMING_ALL_GROUPS;
449 
450  b_info[i].total_time += a_info[i].total_time;
451 
452  if (a_info[i].min_time < b_info[i].min_time)
453  {
454  b_info[i].min_time = a_info[i].min_time;
455  b_info[i].min_iter = a_info[i].min_iter;
456  b_info[i].min_rank = a_info[i].min_rank;
457  }
458 
459  if (a_info[i].max_time > b_info[i].max_time)
460  {
461  b_info[i].max_time = a_info[i].max_time;
462  b_info[i].max_iter = a_info[i].max_iter;
463  b_info[i].max_rank = a_info[i].max_rank;
464  }
465 
466  /* Handle running update to mean and variance */
467  {
468  double cnt_a = a_info[i].iter_count;
469  double cnt_b = b_info[i].iter_count;
470  double avg_a = a_info[i].running_mean;
471  double avg_b = b_info[i].running_mean;
472  double var_a = a_info[i].running_var;
473  double var_b = b_info[i].running_var;
474 
475  double avg, var;
476  double cnt = cnt_a + cnt_b;
477  double cnt_ratio = cnt_a > cnt_b ? cnt_a / cnt_b : cnt_b / cnt_a;
478  double delta = avg_b - avg_a;
479 
480  if (cnt_ratio < 1.01 && cnt_a > 1e+4)
481  avg = (cnt_a * avg_a + cnt_b * avg_b) / cnt;
482  else
483  avg = avg_a + delta * cnt_b / cnt;
484 
485  if (cnt < 2)
486  var = 0;
487  else
488  var = var_a + var_b + delta * delta * cnt_a * cnt_b / cnt;
489 
490  b_info[i].iter_count = cnt;
491  b_info[i].running_mean = avg;
492  b_info[i].running_var = var;
493  }
494  }
495 }
496 #endif
497 
498 void
500 #ifdef HAVE_MPI
501  MPI_Comm comm,
502 #else
503  int comm,
504 #endif
505  int root
506 )
507 {
508  static int first = 1;
509 #ifdef HAVE_MPI
510  static MPI_Op timerinfo_reduce_op;
511  static MPI_Datatype str_32_mpi_type;
512  static MPI_Datatype str_64_mpi_type;
513  static MPI_Datatype timerinfo_mpi_type;
514  int i, rank = 0;
515 
516  MPI_Comm_rank(comm, &rank);
517 
518  if (root == -1)
519  {
520  MPI_Op_free(&timerinfo_reduce_op);
521  MPI_Type_free(&str_32_mpi_type);
522  MPI_Type_free(&str_64_mpi_type);
523  MPI_Type_free(&timerinfo_mpi_type);
524  first = 1;
525  return;
526  }
527 
528  if (first)
529  {
530  int i;
531  MPI_Aint offsets[5];
532  int lengths[5];
533  MPI_Datatype types[5];
534 
535  MPI_Op_create(reduce_a_timerinfo, 0, &timerinfo_reduce_op);
536  MPI_Type_contiguous(32, MPI_CHAR, &str_32_mpi_type);
537  MPI_Type_commit(&str_32_mpi_type);
538  MPI_Type_contiguous(64, MPI_CHAR, &str_64_mpi_type);
539  MPI_Type_commit(&str_64_mpi_type);
540 
541  lengths[0] = 9;
542  types[0] = MPI_INT;
543  MPI_Address(&timerHashTable[0], offsets);
544  lengths[1] = 7;
545  types[1] = MPI_DOUBLE;
546  MPI_Address(&timerHashTable[0].total_time, offsets+1);
547  lengths[2] = 1;
548  types[2] = MPI_UNSIGNED_LONG_LONG;
549  MPI_Address(&timerHashTable[0].gmask, offsets+2);
550  lengths[3] = 1;
551  types[3] = str_32_mpi_type;
552  MPI_Address(&timerHashTable[0].__file__[0], offsets+3);
553  lengths[4] = 1;
554  types[4] = str_64_mpi_type;
555  MPI_Address(&timerHashTable[0].label[0], offsets+4);
556  for (i = 4; i >= 0; offsets[i] -= offsets[0], i--);
557  MPI_Type_struct(5, lengths, offsets, types, &timerinfo_mpi_type);
558  MPI_Type_commit(&timerinfo_mpi_type);
559 
560  first = 0;
561  }
562 
563  clear_timers(reducedTimerTable, MACSIO_TIMING_ALL_GROUPS);
564  for (i = 0; i < MACSIO_TIMING_HASH_TABLE_SIZE; i++)
565  timerHashTable[i].min_rank = timerHashTable[i].max_rank = rank;
566 
567  MPI_Reduce(timerHashTable, reducedTimerTable, MACSIO_TIMING_HASH_TABLE_SIZE,
568  timerinfo_mpi_type, timerinfo_reduce_op, root, comm);
569 #endif
570 }
571 
572 static void
574  timerInfo_t const *table,
576  char ***strs,
577  int *nstrs,
578  int *maxlen
579 )
580 {
581  char **_strs;
582  int pass, _nstrs, _maxlen = 0;
583  int const max_str_size = 1024;
584 
585  for (pass = 0; pass < 2; pass++)
586  {
587  int i;
588 
589  _nstrs = 0;
590  for (i = 0; i < MACSIO_TIMING_HASH_TABLE_SIZE; i++)
591  {
592  int len;
593  double min_in_stddev_steps_from_mean = 0, max_in_stddev_steps_from_mean = 0;
594  double dev;
595 
596  if (!strlen(table[i].label)) continue;
597 
598  if (!(table[i].gmask & gmask)) continue;
599 
600  _nstrs++;
601  if (pass == 0) continue; /* only count the strings on first pass */
602 
603  _strs[_nstrs-1] = (char *) malloc(max_str_size);
604 
605  dev = sqrt(table[i].running_var);
606  if (dev > 0)
607  {
608  min_in_stddev_steps_from_mean = (table[i].running_mean - table[i].min_time) / dev;
609  max_in_stddev_steps_from_mean = (table[i].max_time - table[i].running_mean) / dev;
610  }
611 
612 #warning USE COLUMN HEADINGS INSTEAD
613 #warning HANDLE INDENTATION HERE
614  len = snprintf(_strs[_nstrs-1], max_str_size,
615  "TOT=%10.5f,CNT=%04d,MIN=%8.5f(%4.2f):%06d,AVG=%8.5f,MAX=%8.5f(%4.2f):%06d,DEV=%8.8f:FILE=%s:LINE=%d:LAB=%s",
616  table[i].total_time,
617  table[i].iter_count,
618  table[i].min_time, min_in_stddev_steps_from_mean, table[i].min_rank,
619  table[i].running_mean,
620  table[i].max_time, max_in_stddev_steps_from_mean, table[i].max_rank,
621  dev,
622  table[i].__file__,
623  table[i].__line__,
624  table[i].label);
625 
626  if (len > _maxlen) _maxlen = len;
627  }
628 
629  if (pass == 0)
630  _strs = (char **) malloc(_nstrs * sizeof(char*));
631  }
632 
633  *strs = _strs;
634  *nstrs = _nstrs;
635  *maxlen = _maxlen;
636 }
637 
638 void
641  char ***strs,
642  int *nstrs,
643  int *maxlen
644 )
645 {
646  dump_timers_to_strings(timerHashTable, gmask, strs, nstrs, maxlen);
647 }
648 
651  char ***strs,
652  int *nstrs,
653  int *maxlen
654 )
655 {
656  dump_timers_to_strings(reducedTimerTable, gmask, strs, nstrs, maxlen);
657 }
658 
660 {
661  clear_timers(timerHashTable, gmask);
662  clear_timers(reducedTimerTable, MACSIO_TIMING_ALL_GROUPS);
663 }
664 
666 {
667  return get_current_time();
668 }
double running_mean
void MACSIO_TIMING_ReduceTimers(MPI_Comm comm, int root)
Reduce timers across MPI tasks.
#define MACSIO_TIMING_HASH_TABLE_SIZE
Definition: macsio_timing.c:42
double MACSIO_TIMING_GetReducedTimer(MACSIO_TIMING_TimerId_t tid, char const *field)
Get data from a specific reduced timer.
void MACSIO_TIMING_ClearTimers(MACSIO_TIMING_GroupMask_t gmask)
Clear a group of timers.
#define MACSIO_TIMING_ALL_GROUPS
Group mask representing all groups.
double start_time
static double get_timer(timerInfo_t const *table, MACSIO_TIMING_TimerId_t tid, char const *field)
#define MACSIO_TIMING_INVALID_TIMER
Maybe returned from StartTimer()
MACSIO_TIMING_GroupMask_t gmask
unsigned long long MACSIO_TIMING_GroupMask_t
struct _timerInfo_t timerInfo_t
unsigned int MACSIO_TIMING_TimerId_t
double total_time_this_iter
double MACSIO_TIMING_GetCurrentTime(void)
Get current time.
MACSIO_TIMING_TimerId_t MACSIO_TIMING_StartTimer(char const *label, MACSIO_TIMING_GroupMask_t gmask, int iter_num, char const *__file__, int __line__)
Create/Start a timer.
char __file__[32]
static timerInfo_t reducedTimerTable[MACSIO_TIMING_HASH_TABLE_SIZE]
int MACSIO_TIMING_UseMPI_Wtime
Integer variable to control function used to get timer values.
Definition: macsio_timing.c:44
double running_var
static MACSIO_TIMING_GroupMask_t group_mask_from_name(char const *grpName)
void MACSIO_TIMING_DumpReducedTimersToStrings(MACSIO_TIMING_GroupMask_t gmask, char ***strs, int *nstrs, int *maxlen)
Dump reduced timers to ascii strings.
static int timerGroupCount
double total_time
unsigned int MACSIO_UTILS_BJHash(const unsigned char *k, unsigned int length, unsigned int initval)
Definition: macsio_utils.c:62
static void reduce_a_timerinfo(void *a, void *b, int *len, MPI_Datatype *type)
double MACSIO_TIMING_StopTimer(MACSIO_TIMING_TimerId_t tid)
Stop a timer.
static double get_current_time()
Definition: macsio_timing.c:46
static int const maxTimerGroups
char label[64]
static timerInfo_t timerHashTable[MACSIO_TIMING_HASH_TABLE_SIZE]
double MACSIO_TIMING_GetTimer(MACSIO_TIMING_TimerId_t tid, char const *field)
Get data from a specific timer.
static void clear_timers(timerInfo_t *table, MACSIO_TIMING_GroupMask_t gmask)
void MACSIO_TIMING_DumpTimersToStrings(MACSIO_TIMING_GroupMask_t gmask, char ***strs, int *nstrs, int *maxlen)
Dump timers to ascii strings.
static void dump_timers_to_strings(timerInfo_t const *table, MACSIO_TIMING_GroupMask_t gmask, char ***strs, int *nstrs, int *maxlen)
MACSIO_TIMING_GroupMask_t MACSIO_TIMING_GroupMask(char const *grpName)
Create a group name and mask.
static char * timerGroupNames[maxTimerGroups]
#define MACSIO_TIMING_ITER_AUTO
Automatic iteration numbering Use for iter argument to StartTimer() when you don't want to manager it...
Definition: macsio_timing.h:97