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 EXTRACT_WORDS64(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
__isnan_inl(double d)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
__isinf_ns2(double d)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
__finite_inl(double d)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))
kernel_standard(double x,double y,int z)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
remainder_test1(double x)129 remainder_test1 (double x)
130 {
131   double y = rem1;
132   if (((__builtin_expect (y == 0.0, 0) && !__isnan_inl (x))
133 	|| (__builtin_expect (__isinf_ns2 (x), 0) && !__isnan_inl (y))))
134     return kernel_standard (x, y, 10);
135 
136   return remainder (x, y);
137 }
138 
139 static __always_inline double
remainder_test2(double x)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, 10);
146 
147   return remainder (x, 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
do_one_test(json_ctx_t * json_ctx,proto_t test_fn,volatile double * arr,size_t len,const char * testname)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 (json_ctx, 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 (json_ctx, "duration", cur);
234   json_attr_double (json_ctx, "iterations", iters);
235   json_attr_double (json_ctx, "mean", cur / iters);
236   json_attr_object_end (json_ctx);
237 }
238 
239 static volatile double arr1[SIZE];
240 static volatile double arr2[SIZE];
241 
242 int
test_main(void)243 test_main (void)
244 {
245   json_ctx_t json_ctx;
246   size_t i;
247 
248   bench_start ();
249 
250   json_init (&json_ctx, 2, stdout);
251   json_attr_object_begin (&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 (&json_ctx, test_list[i].name);
269       do_one_test (&json_ctx, test_list[i].fn, arr2, SIZE, "inf/nan");
270       json_attr_object_end (&json_ctx);
271     }
272 
273   for (i = 0; i < sizeof (test_list) / sizeof (test_list[0]); i++)
274     {
275       json_attr_object_begin (&json_ctx, test_list[i].name);
276       do_one_test (&json_ctx, test_list[i].fn, arr1, SIZE, "normal");
277       json_attr_object_end (&json_ctx);
278     }
279 
280   json_attr_object_end (&json_ctx);
281   return 0;
282 }
283 
284 #include "bench-util.c"
285 #include "../test-skeleton.c"
286