1 /*
2  *	Copyright Jan Engelhardt, 2012
3  *
4  *	This program is free software; you can redistribute it and/or
5  *	modify it under the terms of the WTF Public License version 2 or
6  *	(at your option) any later version.
7  */
8 #include <math.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <libHX/defs.h>
14 #include <libHX/init.h>
15 #include <libHX/misc.h>
16 #include "internal.h"
17 
18 typedef struct timespec *(*add_func_t)(struct timespec *,
19 	const struct timespec *, const struct timespec *);
20 typedef struct timespec *(*mul_func_t)(struct timespec *,
21 	const struct timespec *, int);
22 typedef struct timespec *(*mulf_func_t)(struct timespec *,
23 	const struct timespec *, double);
24 
25 static const int NANOSECOND = 1000000000;
26 static const long long NANOSECOND_LL = 1000000000;
27 static const unsigned int clock_id = CLOCK_THREAD_CPUTIME_ID;
28 static const unsigned int step = 1000;
29 static const unsigned int step_mul = 10000000;
30 
31 static const struct timespec pairs[] = {
32 	{-1, 700000000}, {-1, 400000000}, {-1, 0},
33 	{0, -700000000}, {0, -400000000}, {0, 0},
34 	{0, 400000000}, {0, 700000000},
35 	{1, 0}, {1, 400000000}, {1, 700000000},
36 };
37 
38 /*
39  * Variant that uses full 64 bit division and is thus slower on
40  * a handful of hardware.
41  */
HX_timespec_add_FDIV(struct timespec * r,const struct timespec * a,const struct timespec * b)42 static struct timespec *HX_timespec_add_FDIV(struct timespec *r,
43     const struct timespec *a, const struct timespec *b)
44 {
45 	long long p, q;
46 
47 	p  = a->tv_sec * NANOSECOND_LL +
48 	     ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec);
49 	q  = b->tv_sec * NANOSECOND_LL +
50 	     ((b->tv_sec >= 0) ? b->tv_nsec : -b->tv_nsec);
51 
52 	p += q;
53 	r->tv_sec  = p / NANOSECOND;
54 	r->tv_nsec = p % NANOSECOND;
55 	if (r->tv_sec < 0 && r->tv_nsec < 0)
56 		r->tv_nsec = -r->tv_nsec;
57 	return r;
58 }
59 
60 /*
61  * Variant that does split multiplication.
62  */
63 static struct timespec *
HX_timespec_mul_SPL(struct timespec * r,const struct timespec * a,int f)64 HX_timespec_mul_SPL(struct timespec *r, const struct timespec *a, int f)
65 {
66 	long long nsec;
67 	bool neg = HX_timespec_isneg(a);
68 
69 	if (neg)
70 		HX_timespec_neg(r, a);
71 	else
72 		*r = *a;
73 	if (f < 0) {
74 		f = -f;
75 		neg = !neg;
76 	}
77 
78 	r->tv_sec *= f;
79 	nsec = static_cast(long long, r->tv_nsec) * f;
80 	r->tv_sec += nsec / NANOSECOND;
81 	r->tv_nsec = nsec % NANOSECOND;
82 	if (neg)
83 		HX_timespec_neg(r, r);
84 	return r;
85 }
86 
87 /*
88  * Variant for mulf that uses seconds rather than nanosecond as working base.
89  * This shows itself to be detrimental to precision (on IEEE-754).
90  */
91 static struct timespec *
HX_timespec_mulf_S(struct timespec * r,const struct timespec * a,double f)92 HX_timespec_mulf_S(struct timespec *r, const struct timespec *a, double f)
93 {
94 	double i, t;
95 
96 	t  = ((a->tv_sec >= 0) ? a->tv_nsec : -a->tv_nsec) /
97 	     static_cast(double, NANOSECOND);
98 	t += a->tv_sec;
99 	t *= f;
100 	t  = modf(t, &i);
101 	r->tv_nsec = t * NANOSECOND;
102 	r->tv_sec  = i;
103 	if (r->tv_sec < 0 && r->tv_nsec < 0)
104 		r->tv_nsec = -r->tv_nsec;
105 	return r;
106 }
107 
test_same(void)108 static void test_same(void)
109 {
110 	static const struct timespec zero = {0, 0};
111 	struct timespec r;
112 	unsigned int i;
113 
114 	printf("# Test src==dst operand behavior\n");
115 
116 	/* 1s */
117 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
118 		r = pairs[i];
119 		printf("-(" HX_TIMESPEC_FMT ") = ", HX_TIMESPEC_EXP(&r));
120 		HX_timespec_neg(&r, &r);
121 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
122 	}
123 	printf("\n");
124 
125 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
126 		r = pairs[i];
127 		printf(HX_TIMESPEC_FMT " + 0 = ", HX_TIMESPEC_EXP(&r));
128 		HX_timespec_add(&r, &r, &zero);
129 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
130 	}
131 	printf("\n");
132 
133 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
134 		r = pairs[i];
135 		printf(HX_TIMESPEC_FMT " - 0 = ", HX_TIMESPEC_EXP(&r));
136 		HX_timespec_sub(&r, &r, &zero);
137 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
138 	}
139 	printf("\n");
140 
141 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
142 		r = pairs[i];
143 		printf(HX_TIMESPEC_FMT " * 1 = ", HX_TIMESPEC_EXP(&r));
144 		HX_timespec_mul(&r, &r, 1);
145 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
146 	}
147 	printf("\n");
148 
149 	/* 2s */
150 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
151 		r = pairs[i];
152 		printf(HX_TIMESPEC_FMT " + " HX_TIMESPEC_FMT " = ",
153 		       HX_TIMESPEC_EXP(&r), HX_TIMESPEC_EXP(&r));
154 		HX_timespec_add(&r, &r, &r);
155 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
156 	}
157 	printf("\n");
158 
159 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
160 		r = pairs[i];
161 		printf(HX_TIMESPEC_FMT " - " HX_TIMESPEC_FMT " = ",
162 		       HX_TIMESPEC_EXP(&r), HX_TIMESPEC_EXP(&r));
163 		HX_timespec_sub(&r, &r, &r);
164 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
165 	}
166 	printf("\n");
167 }
168 
print_sgn(const struct timespec * a)169 static void print_sgn(const struct timespec *a)
170 {
171 	printf(HX_timespec_isneg(a) ? "[-]" : "[+]");
172 }
173 
test_neg(void)174 static void test_neg(void)
175 {
176 	const struct timespec *now;
177 	struct timespec then;
178 
179 	printf("# Negation\n");
180 	for (now = pairs; now < pairs + ARRAY_SIZE(pairs); ++now) {
181 		HX_timespec_neg(&then, now);
182 
183 		print_sgn(now);
184 		printf(HX_TIMESPEC_FMT " -> ", HX_TIMESPEC_EXP(now));
185 		print_sgn(&then);
186 		printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&then));
187 	}
188 	printf("\n");
189 }
190 
print_op2(const struct timespec * r,const struct timespec * a,const char * op,const struct timespec * b)191 static void print_op2(const struct timespec *r, const struct timespec *a,
192     const char *op, const struct timespec *b)
193 {
194 	printf(HX_TIMESPEC_FMT " %s ", HX_TIMESPEC_EXP(a), op);
195 	printf(HX_TIMESPEC_FMT " = ", HX_TIMESPEC_EXP(b));
196 	printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(r));
197 }
198 
test_add(void)199 static void test_add(void)
200 {
201 	const struct timespec *a, *b;
202 	struct timespec r, s;
203 
204 	printf("# Test addition behavior\n");
205 	for (a = pairs; a < pairs + ARRAY_SIZE(pairs); ++a) {
206 		for (b = pairs; b < pairs + ARRAY_SIZE(pairs); ++b) {
207 			HX_timespec_add(&r, a, b);
208 			print_op2(&r, a, "+N", b);
209 			HX_timespec_add_FDIV(&s, a, b);
210 			print_op2(&r, a, "+F", b);
211 			if (r.tv_sec != s.tv_sec || r.tv_nsec != s.tv_nsec)
212 				abort();
213 			HX_timespec_sub(&r, a, b);
214 			print_op2(&r, a, "- ", b);
215 			printf("----------\n");
216 		}
217 	}
218 	printf("\n");
219 }
220 
test_adds_nz(time_t s,add_func_t fn)221 static void test_adds_nz(time_t s, add_func_t fn)
222 {
223 	struct timespec a, b, r;
224 
225 	a.tv_sec = s;
226 	for (a.tv_nsec = 0; a.tv_nsec < NANOSECOND;
227 	     a.tv_nsec += NANOSECOND / step)
228 	{
229 		b.tv_sec = -1;
230 		for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
231 		     b.tv_nsec += NANOSECOND / step)
232 			(*fn)(&r, &a, &b);
233 
234 		b.tv_sec = 0;
235 		for (b.tv_nsec = -NANOSECOND + NANOSECOND / step;
236 		     b.tv_nsec < NANOSECOND; b.tv_nsec += NANOSECOND / step)
237 			(*fn)(&r, &a, &b);
238 
239 		b.tv_sec = 1;
240 		for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
241 		     b.tv_nsec += NANOSECOND / step)
242 			(*fn)(&r, &a, &b);
243 	}
244 }
245 
test_adds_z(add_func_t fn)246 static void test_adds_z(add_func_t fn)
247 {
248 	struct timespec a, b, r;
249 
250 	a.tv_sec = 0;
251 	for (a.tv_nsec = -NANOSECOND + NANOSECOND / step;
252 	     a.tv_nsec < NANOSECOND; a.tv_nsec += NANOSECOND / step)
253 	{
254 		b.tv_sec = -1;
255 		for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
256 		     b.tv_nsec += NANOSECOND / step)
257 			(*fn)(&r, &a, &b);
258 
259 		b.tv_sec = 0;
260 		for (b.tv_nsec = -NANOSECOND + NANOSECOND / step;
261 		     b.tv_nsec < NANOSECOND; b.tv_nsec += NANOSECOND / step)
262 			(*fn)(&r, &a, &b);
263 
264 		b.tv_sec = 1;
265 		for (b.tv_nsec = 0; b.tv_nsec < NANOSECOND;
266 		     b.tv_nsec += NANOSECOND / step)
267 			(*fn)(&r, &a, &b);
268 	}
269 }
270 
test_adds_1(const char * text,add_func_t fn)271 static void test_adds_1(const char *text, add_func_t fn)
272 {
273 	struct timespec start, delta;
274 
275 	printf("%s", text);
276 	clock_gettime(clock_id, &start);
277 	test_adds_nz(-2, fn);
278 	test_adds_nz(-1, fn);
279 	test_adds_z(fn);
280 	test_adds_nz(1, fn);
281 	test_adds_nz(2, fn);
282 	clock_gettime(clock_id, &delta);
283 	HX_timespec_sub(&delta, &delta, &start);
284 	printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
285 }
286 
test_adds(void)287 static void test_adds(void)
288 {
289 	printf("# Test addition speed\n");
290 	test_adds_1("normal:  ", HX_timespec_add);
291 	test_adds_1("fulldiv: ", HX_timespec_add_FDIV);
292 	printf("\n");
293 }
294 
test_mul(void)295 static void test_mul(void)
296 {
297 	struct timespec r, s;
298 	unsigned int i;
299 	double k;
300 	int j;
301 
302 	printf("# Test multiplication behavior\n");
303 	for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
304 		for (j = -3; j <= 3; ++j) {
305 			printf(HX_TIMESPEC_FMT " *N %d = ",
306 			       HX_TIMESPEC_EXP(&pairs[i]), j);
307 			HX_timespec_mul(&r, &pairs[i], j);
308 			printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
309 
310 			printf(HX_TIMESPEC_FMT " *S %d = ",
311 			       HX_TIMESPEC_EXP(&pairs[i]), j);
312 			HX_timespec_mul_SPL(&s, &pairs[i], j);
313 			printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&s));
314 
315 			if (r.tv_sec != s.tv_sec || r.tv_nsec != s.tv_nsec)
316 				abort();
317 		}
318 
319 		for (k = -3; k <= 3; k += 0.1) {
320 			printf(HX_TIMESPEC_FMT " *fN %f = ",
321 			       HX_TIMESPEC_EXP(&pairs[i]), k);
322 			HX_timespec_mulf(&r, &pairs[i], k);
323 			printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&r));
324 
325 			printf(HX_TIMESPEC_FMT " *fS %f = ",
326 			       HX_TIMESPEC_EXP(&pairs[i]), k);
327 			HX_timespec_mulf_S(&s, &pairs[i], k);
328 			printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&s));
329 		}
330 	}
331 
332 	printf("\n");
333 }
334 
test_muls_1i(const char * text,mul_func_t fn)335 static void test_muls_1i(const char *text, mul_func_t fn)
336 {
337 	struct timespec r, s, start, delta;
338 	unsigned int i;
339 
340 	printf("%s", text);
341 	clock_gettime(clock_id, &start);
342 	for (i = 0; i < step_mul; ++i) {
343 		r.tv_sec  = -i;
344 		r.tv_nsec = -i / 4;
345 		(*fn)(&s, &r, 7);
346 	}
347 	clock_gettime(clock_id, &delta);
348 	HX_timespec_sub(&delta, &delta, &start);
349 	printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
350 }
351 
test_muls_1f(const char * text,mulf_func_t fn)352 static void test_muls_1f(const char *text, mulf_func_t fn)
353 {
354 	struct timespec r, s, start, delta;
355 	unsigned int i;
356 
357 	printf("%s", text);
358 	clock_gettime(clock_id, &start);
359 	for (i = 0; i < step_mul; ++i) {
360 		r.tv_sec  = -i;
361 		r.tv_nsec = -i / 4;
362 		(*fn)(&s, &r, 7);
363 	}
364 	clock_gettime(clock_id, &delta);
365 	HX_timespec_sub(&delta, &delta, &start);
366 	printf(HX_TIMESPEC_FMT "\n", HX_TIMESPEC_EXP(&delta));
367 }
368 
test_muls(void)369 static void test_muls(void)
370 {
371 	printf("# Test multiplication speed\n");
372 	test_muls_1i("normal: ", HX_timespec_mul);
373 	test_muls_1i("split:  ", HX_timespec_mul_SPL);
374 
375 	test_muls_1f("float:  ", HX_timespec_mulf);
376 	test_muls_1f("flt-S:  ", HX_timespec_mulf_S);
377 	printf("\n");
378 }
379 
main(void)380 int main(void)
381 {
382 	if (HX_init() <= 0)
383 		abort();
384 
385 	test_same();
386 	test_neg();
387 	test_add();
388 	test_mul();
389 	test_adds();
390 	test_muls();
391 	HX_exit();
392 	return EXIT_SUCCESS;
393 }
394