1/* Measure math inline functions.
2 Copyright (C) 2015-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#define SIZE 1024
20#define TEST_MAIN
21#define TEST_NAME "math-inlines"
22#define TEST_FUNCTION test_main ()
23#include "bench-timing.h"
24#include "json-lib.h"
25#include "bench-util.h"
26
27#include <stdlib.h>
28#include <math.h>
29#include <stdint.h>
30
31#define BOOLTEST(func) \
32static int __attribute__((noinline)) \
33func ## _f (double d, int i) \
34{ \
35 if (func (d)) \
36 return (int) d + i; \
37 else \
38 return 5; \
39} \
40static int \
41func ## _t (volatile double *p, size_t n, size_t iters) \
42{ \
43 int i, j; \
44 int res = 0; \
45 for (j = 0; j < iters; j++) \
46 for (i = 0; i < n; i++) \
47 if (func ## _f (p[i] * 2.0, i) != 0) \
48 res += 5; \
49 return res; \
50}
51
52#define VALUETEST(func) \
53static int __attribute__((noinline)) \
54func ## _f (double d) \
55{ \
56 return func (d); \
57} \
58static int \
59func ## _t (volatile double *p, size_t n, size_t iters) \
60{ \
61 int i, j; \
62 int res = 0; \
63 for (j = 0; j < iters; j++) \
64 for (i = 0; i < n; i++) \
65 res += func ## _f (p[i] * 2.0); \
66 return res; \
67}
68
69typedef union
70{
71 double value;
72 uint64_t word;
73} ieee_double_shape_type;
74
75#define EXTRACT_WORDS64(i,d) \
76do { \
77 ieee_double_shape_type gh_u; \
78 gh_u.value = (d); \
79 (i) = gh_u.word; \
80} while (0)
81
82/* Inlines similar to existing math_private.h versions. */
83
84static __always_inline int
85__isnan_inl (double d)
86{
87 uint64_t di;
88 EXTRACT_WORDS64 (di, d);
89 return (di & 0x7fffffffffffffffull) > 0x7ff0000000000000ull;
90}
91
92static __always_inline int
93__isinf_ns2 (double d)
94{
95 uint64_t di;
96 EXTRACT_WORDS64 (di, d);
97 return (di & 0x7fffffffffffffffull) == 0x7ff0000000000000ull;
98}
99
100static __always_inline int
101__finite_inl (double d)
102{
103 uint64_t di;
104 EXTRACT_WORDS64 (di, d);
105 return (di & 0x7fffffffffffffffull) < 0x7ff0000000000000ull;
106}
107
108#define __isnormal_inl(X) (__fpclassify (X) == FP_NORMAL)
109
110/* Inlines for the builtin functions. */
111
112#define __isnan_builtin(X) __builtin_isnan (X)
113#define __isinf_ns_builtin(X) __builtin_isinf (X)
114#define __isinf_builtin(X) __builtin_isinf_sign (X)
115#define __isfinite_builtin(X) __builtin_isfinite (X)
116#define __isnormal_builtin(X) __builtin_isnormal (X)
117#define __fpclassify_builtin(X) __builtin_fpclassify (FP_NAN, FP_INFINITE, \
118 FP_NORMAL, FP_SUBNORMAL, FP_ZERO, (X))
119
120static double __attribute ((noinline))
121kernel_standard (double x, double y, int z)
122{
123 return x * y + z;
124}
125
126volatile double rem1 = 2.5;
127
128static __always_inline double
129remainder_test1 (double x)
130{
131 double y = rem1;
132 if (((__builtin_expect (y == 0.0, 0) && !__isnan_inl (d: x))
133 || (__builtin_expect (__isinf_ns2 (d: x), 0) && !__isnan_inl (d: y))))
134 return kernel_standard (x, y, z: 10);
135
136 return remainder (x: x, y: y);
137}
138
139static __always_inline double
140remainder_test2 (double x)
141{
142 double y = rem1;
143 if (((__builtin_expect (y == 0.0, 0) && !__builtin_isnan (x))
144 || (__builtin_expect (__builtin_isinf (x), 0) && !__builtin_isnan (y))))
145 return kernel_standard (x, y, z: 10);
146
147 return remainder (x: x, y: y);
148}
149
150/* Create test functions for each possibility. */
151
152BOOLTEST (__isnan)
153BOOLTEST (__isnan_inl)
154BOOLTEST (__isnan_builtin)
155BOOLTEST (isnan)
156
157BOOLTEST (__isinf)
158BOOLTEST (__isinf_builtin)
159BOOLTEST (__isinf_ns2)
160BOOLTEST (__isinf_ns_builtin)
161BOOLTEST (isinf)
162
163BOOLTEST (__finite)
164BOOLTEST (__finite_inl)
165BOOLTEST (__isfinite_builtin)
166BOOLTEST (isfinite)
167
168BOOLTEST (__isnormal_inl)
169BOOLTEST (__isnormal_builtin)
170BOOLTEST (isnormal)
171
172VALUETEST (__fpclassify)
173VALUETEST (__fpclassify_builtin)
174VALUETEST (fpclassify)
175
176VALUETEST (remainder_test1)
177VALUETEST (remainder_test2)
178
179typedef int (*proto_t) (volatile double *p, size_t n, size_t iters);
180
181typedef struct
182{
183 const char *name;
184 proto_t fn;
185} impl_t;
186
187#define IMPL(name) { #name, name ## _t }
188
189static impl_t test_list[] =
190{
191 IMPL (__isnan),
192 IMPL (__isnan_inl),
193 IMPL (__isnan_builtin),
194 IMPL (isnan),
195
196 IMPL (__isinf),
197 IMPL (__isinf_ns2),
198 IMPL (__isinf_ns_builtin),
199 IMPL (__isinf_builtin),
200 IMPL (isinf),
201
202 IMPL (__finite),
203 IMPL (__finite_inl),
204 IMPL (__isfinite_builtin),
205 IMPL (isfinite),
206
207 IMPL (__isnormal_inl),
208 IMPL (__isnormal_builtin),
209 IMPL (isnormal),
210
211 IMPL (__fpclassify),
212 IMPL (__fpclassify_builtin),
213 IMPL (fpclassify),
214
215 IMPL (remainder_test1),
216 IMPL (remainder_test2)
217};
218
219static void
220do_one_test (json_ctx_t *json_ctx, proto_t test_fn, volatile double *arr,
221 size_t len, const char *testname)
222{
223 size_t iters = 2048;
224 timing_t start, stop, cur;
225
226 json_attr_object_begin (ctx: json_ctx, name: testname);
227
228 TIMING_NOW (start);
229 test_fn (arr, len, iters);
230 TIMING_NOW (stop);
231 TIMING_DIFF (cur, start, stop);
232
233 json_attr_double (ctx: json_ctx, name: "duration", d: cur);
234 json_attr_double (ctx: json_ctx, name: "iterations", d: iters);
235 json_attr_double (ctx: json_ctx, name: "mean", d: cur / iters);
236 json_attr_object_end (ctx: json_ctx);
237}
238
239static volatile double arr1[SIZE];
240static volatile double arr2[SIZE];
241
242int
243test_main (void)
244{
245 json_ctx_t json_ctx;
246 size_t i;
247
248 bench_start ();
249
250 json_init (ctx: &json_ctx, indent_level: 2, stdout);
251 json_attr_object_begin (ctx: &json_ctx, TEST_NAME);
252
253 /* Create 2 test arrays, one with 10% zeroes, 10% negative values,
254 79% positive values and 1% infinity/NaN. The other contains
255 50% inf, 50% NaN. This relies on rand behaving correctly. */
256
257 for (i = 0; i < SIZE; i++)
258 {
259 int x = rand () & 255;
260 arr1[i] = (x < 25) ? 0.0 : ((x < 50) ? -1 : 100);
261 if (x == 255) arr1[i] = __builtin_inf ();
262 if (x == 254) arr1[i] = __builtin_nan ("0");
263 arr2[i] = (x < 128) ? __builtin_inf () : __builtin_nan ("0");
264 }
265
266 for (i = 0; i < sizeof (test_list) / sizeof (test_list[0]); i++)
267 {
268 json_attr_object_begin (ctx: &json_ctx, name: test_list[i].name);
269 do_one_test (json_ctx: &json_ctx, test_fn: test_list[i].fn, arr: arr2, SIZE, testname: "inf/nan");
270 json_attr_object_end (ctx: &json_ctx);
271 }
272
273 for (i = 0; i < sizeof (test_list) / sizeof (test_list[0]); i++)
274 {
275 json_attr_object_begin (ctx: &json_ctx, name: test_list[i].name);
276 do_one_test (json_ctx: &json_ctx, test_fn: test_list[i].fn, arr: arr1, SIZE, testname: "normal");
277 json_attr_object_end (ctx: &json_ctx);
278 }
279
280 json_attr_object_end (ctx: &json_ctx);
281 return 0;
282}
283
284#include "bench-util.c"
285#include "../test-skeleton.c"
286

source code of glibc/benchtests/bench-math-inlines.c