xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Measure math inline functions. |
| 2 | Copyright (C) 2015-2016 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 | <http://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 |
| 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 (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 |
| 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 |
| 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 = 500; |
| 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 |
| 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" |