aboutsummaryrefslogtreecommitdiff
path: root/src/timings.cpp
blob: 89280b779c1c7b948266bf5a2fe47fe5a2f9ca07 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
struct TimeStamp {
	u64    start;
	u64    finish;
	String label;
};

struct Timings {
	TimeStamp        total;
	Array<TimeStamp> sections;
	u64              freq;
	f64              total_time_seconds;
};


#if defined(GB_SYSTEM_WINDOWS)
u64 win32_time_stamp_time_now(void) {
	LARGE_INTEGER counter;
	QueryPerformanceCounter(&counter);
	return counter.QuadPart;
}

u64 win32_time_stamp__freq(void) {
	gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0};
	if (!win32_perf_count_freq.QuadPart) {
		QueryPerformanceFrequency(&win32_perf_count_freq);
		GB_ASSERT(win32_perf_count_freq.QuadPart != 0);
	}

	return win32_perf_count_freq.QuadPart;
}

#elif defined(GB_SYSTEM_OSX)

#include <mach/mach_time.h>

u64 osx_time_stamp_time_now(void) {
	return mach_absolute_time();
}

u64 osx_time_stamp__freq(void) {
	mach_timebase_info_data_t data;
	data.numer = 0;
	data.denom = 0;
	mach_timebase_info(&data);
	return (data.numer / data.denom) * 1000000000;
}

#elif defined(GB_SYSTEM_UNIX)

#include <time.h>

u64 unix_time_stamp_time_now(void) {
	struct timespec ts;
	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);

	return (ts.tv_sec * 1000000000) + ts.tv_nsec;
}

u64 unix_time_stamp__freq(void) {
	gb_local_persist u64 freq = 0;

	if (freq == 0) {
		struct timespec ts;
		clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
		freq = cast(u64) ((1.0 / ts.tv_nsec) * 1000000000.0);
	}

	return freq;
}



#else
#error Implement system
#endif

u64 time_stamp_time_now(void) {
#if defined(GB_SYSTEM_WINDOWS)
	return win32_time_stamp_time_now();
#elif defined(GB_SYSTEM_OSX)
	return osx_time_stamp_time_now();
#elif defined(GB_SYSTEM_UNIX)
	return unix_time_stamp_time_now();
#else
#error time_stamp_time_now
#endif
}

u64 time_stamp__freq(void) {
#if defined(GB_SYSTEM_WINDOWS)
	return win32_time_stamp__freq();
#elif defined(GB_SYSTEM_OSX)
	return osx_time_stamp__freq();
#elif defined(GB_SYSTEM_UNIX)
	return unix_time_stamp__freq();
#else
#error time_stamp__freq
#endif
}

TimeStamp make_time_stamp(String label) {
	TimeStamp ts = {0};
	ts.start = time_stamp_time_now();
	ts.label = label;
	return ts;
}

void timings_init(Timings *t, String label, isize buffer_size) {
	array_init(&t->sections, heap_allocator(), 0, buffer_size);
	t->total = make_time_stamp(label);
	t->freq  = time_stamp__freq();
}

void timings_destroy(Timings *t) {
	array_free(&t->sections);
}

void timings__stop_current_section(Timings *t) {
	if (t->sections.count > 0) {
		t->sections[t->sections.count-1].finish = time_stamp_time_now();
	}
}

void timings_start_section(Timings *t, String label) {
	timings__stop_current_section(t);
	array_add(&t->sections, make_time_stamp(label));
}

f64 time_stamp_as_s(TimeStamp const &ts, u64 freq) {
	GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
	return cast(f64)(ts.finish - ts.start) / cast(f64)freq;
}

f64 time_stamp_as_ms(TimeStamp const &ts, u64 freq) {
	return 1000.0*time_stamp_as_s(ts, freq);
}

f64 time_stamp_as_us(TimeStamp const &ts, u64 freq) {
	return 1000000.0*time_stamp_as_s(ts, freq);
}

enum TimingUnit {
	TimingUnit_Second,
	TimingUnit_Millisecond,
	TimingUnit_Microsecond,

	TimingUnit_COUNT,
};

char const *timing_unit_strings[TimingUnit_COUNT] = {"s", "ms", "us"};

f64 time_stamp(TimeStamp const &ts, u64 freq, TimingUnit unit) {
	switch (unit) {
	case TimingUnit_Millisecond: return time_stamp_as_ms(ts, freq);
	case TimingUnit_Microsecond: return time_stamp_as_us(ts, freq);
	default: /*fallthrough*/
	case TimingUnit_Second:      return time_stamp_as_s (ts, freq);
	}
}

void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Millisecond) {
	char const SPACES[] = "                                                                ";
	isize max_len;

	timings__stop_current_section(t);
	t->total.finish = time_stamp_time_now();

	max_len = t->total.label.len;
	max_len = 36;
	for_array(i, t->sections) {
		TimeStamp ts = t->sections[i];
		max_len = gb_max(max_len, ts.label.len);
	}

	GB_ASSERT(max_len <= gb_size_of(SPACES)-1);

	t->total_time_seconds = time_stamp_as_s(t->total, t->freq);

	f64 total_time = time_stamp(t->total, t->freq, unit);

	gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
	          LIT(t->total.label),
	          cast(int)(max_len-t->total.label.len), SPACES,
	          total_time,
	          timing_unit_strings[unit],
	          cast(f64)100.0);

	for_array(i, t->sections) {
		TimeStamp ts = t->sections[i];
		f64 section_time = time_stamp(ts, t->freq, unit);
		gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
		          LIT(ts.label),
	              cast(int)(max_len-ts.label.len), SPACES,
		          section_time,
		          timing_unit_strings[unit],
		          100.0*section_time/total_time);
	}
}