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) \ |
32 | static int __attribute__((noinline)) \ |
33 | func ## _f (double d, int i) \ |
34 | { \ |
35 | if (func (d)) \ |
36 | return (int) d + i; \ |
37 | else \ |
38 | return 5; \ |
39 | } \ |
40 | static int \ |
41 | func ## _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) \ |
53 | static int __attribute__((noinline)) \ |
54 | func ## _f (double d) \ |
55 | { \ |
56 | return func (d); \ |
57 | } \ |
58 | static int \ |
59 | func ## _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 | |
69 | typedef union |
70 | { |
71 | double value; |
72 | uint64_t word; |
73 | } ieee_double_shape_type; |
74 | |
75 | #define (i,d) \ |
76 | do { \ |
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 | |
84 | static __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 | |
92 | static __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 | |
100 | static __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 | |
120 | static double __attribute ((noinline)) |
121 | kernel_standard (double x, double y, int z) |
122 | { |
123 | return x * y + z; |
124 | } |
125 | |
126 | volatile double rem1 = 2.5; |
127 | |
128 | static __always_inline double |
129 | remainder_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 | |
139 | static __always_inline double |
140 | remainder_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 | |
152 | BOOLTEST (__isnan) |
153 | BOOLTEST (__isnan_inl) |
154 | BOOLTEST (__isnan_builtin) |
155 | BOOLTEST (isnan) |
156 | |
157 | BOOLTEST (__isinf) |
158 | BOOLTEST (__isinf_builtin) |
159 | BOOLTEST (__isinf_ns2) |
160 | BOOLTEST (__isinf_ns_builtin) |
161 | BOOLTEST (isinf) |
162 | |
163 | BOOLTEST (__finite) |
164 | BOOLTEST (__finite_inl) |
165 | BOOLTEST (__isfinite_builtin) |
166 | BOOLTEST (isfinite) |
167 | |
168 | BOOLTEST (__isnormal_inl) |
169 | BOOLTEST (__isnormal_builtin) |
170 | BOOLTEST (isnormal) |
171 | |
172 | VALUETEST (__fpclassify) |
173 | VALUETEST (__fpclassify_builtin) |
174 | VALUETEST (fpclassify) |
175 | |
176 | VALUETEST (remainder_test1) |
177 | VALUETEST (remainder_test2) |
178 | |
179 | typedef int (*proto_t) (volatile double *p, size_t n, size_t iters); |
180 | |
181 | typedef struct |
182 | { |
183 | const char *name; |
184 | proto_t fn; |
185 | } impl_t; |
186 | |
187 | #define IMPL(name) { #name, name ## _t } |
188 | |
189 | static 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 | |
219 | static void |
220 | do_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 | |
239 | static volatile double arr1[SIZE]; |
240 | static volatile double arr2[SIZE]; |
241 | |
242 | int |
243 | test_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 | |