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