summaryrefslogtreecommitdiff
path: root/src/dged/timers.c
blob: 2fd5fc9ab84da85082634ad71c0e349a8d7227a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "timers.h"
#include "hash.h"
#include "hashmap.h"

#include <stdbool.h>
#include <string.h>
#include <time.h>

#define NUM_FRAME_SAMPLES 16

struct timer {
  char name[32];
  uint64_t max;
  uint64_t min;
  uint64_t samples[NUM_FRAME_SAMPLES];
  struct timespec started_at;
};

HASHMAP_ENTRY_TYPE(timer_entry, struct timer);

static struct timers {
  uint32_t frame_index;
  HASHMAP(struct timer_entry) timers;
} g_timers;

void timers_init(void) {
  HASHMAP_INIT(&g_timers.timers, 32, hash_name);
  g_timers.frame_index = 0;
}

void timers_destroy(void) { HASHMAP_DESTROY(&g_timers.timers); }

void timers_start_frame(void) {
  HASHMAP_FOR_EACH(&g_timers.timers, struct timer_entry * entry) {
    struct timer *timer = &entry->value;
    timer->samples[g_timers.frame_index] = 0;
  }
}

void timers_end_frame(void) {
  g_timers.frame_index = (g_timers.frame_index + 1) % NUM_FRAME_SAMPLES;
}

struct timer *timer_start(const char *name) {
  HASHMAP_GET(&g_timers.timers, struct timer_entry, name, struct timer * t);
  if (t == NULL) {
    HASHMAP_APPEND(&g_timers.timers, struct timer_entry, name,
                   struct timer_entry * tnew);
    struct timer *new_timer = &tnew->value;

    size_t namelen = strlen(name);
    namelen = namelen >= 32 ? 31 : namelen;
    memcpy(new_timer->name, name, namelen);
    new_timer->name[namelen] = '\0';
    new_timer->max = 0;
    new_timer->min = (uint64_t)-1;
    memset(new_timer->samples, 0, sizeof(uint64_t) * NUM_FRAME_SAMPLES);

    t = new_timer;
  }

  clock_gettime(CLOCK_MONOTONIC, &t->started_at);
  return t;
}

uint64_t timer_stop(struct timer *timer) {
  struct timespec end;
  clock_gettime(CLOCK_MONOTONIC, &end);
  uint64_t elapsed = ((uint64_t)end.tv_sec * 1e9 + (uint64_t)end.tv_nsec) -
                     ((uint64_t)timer->started_at.tv_sec * 1e9 +
                      (uint64_t)timer->started_at.tv_nsec);

  if (elapsed > timer->max) {
    timer->max = elapsed;
  }

  if (elapsed < timer->min) {
    timer->min = elapsed;
  }

  timer->samples[g_timers.frame_index] += elapsed;
  return elapsed;
}

struct timer *timer_get(const char *name) {
  HASHMAP_GET(&g_timers.timers, struct timer_entry, name, struct timer * t);
  return t;
}

float timer_average(const struct timer *timer) {
  uint64_t sum = 0;
  for (uint32_t samplei = 0; samplei < NUM_FRAME_SAMPLES; ++samplei) {
    sum += timer->samples[samplei];
  }

  return (float)sum / NUM_FRAME_SAMPLES;
}

uint64_t timer_max(const struct timer *timer) { return timer->max; }

uint64_t timer_min(const struct timer *timer) { return timer->min; }

const char *timer_name(const struct timer *timer) { return timer->name; }

void timers_for_each(timer_callback callback, void *userdata) {
  HASHMAP_FOR_EACH(&g_timers.timers, struct timer_entry * entry) {
    const struct timer *timer = &entry->value;
    callback(timer, userdata);
  }
}