| /* Measure math inline functions. |
| Copyright (C) 2015-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #define SIZE 1024 |
| #define TEST_MAIN |
| #define TEST_NAME "math-inlines" |
| #define TEST_FUNCTION test_main () |
| #include "bench-timing.h" |
| #include "json-lib.h" |
| #include "bench-util.h" |
| |
| #include <stdlib.h> |
| #include <math.h> |
| #include <stdint.h> |
| |
| #define BOOLTEST(func) \ |
| static int __attribute__((noinline)) \ |
| func ## _f (double d, int i) \ |
| { \ |
| if (func (d)) \ |
| return (int) d + i; \ |
| else \ |
| return 5; \ |
| } \ |
| static int \ |
| func ## _t (volatile double *p, size_t n, size_t iters) \ |
| { \ |
| int i, j; \ |
| int res = 0; \ |
| for (j = 0; j < iters; j++) \ |
| for (i = 0; i < n; i++) \ |
| if (func ## _f (p[i] * 2.0, i) != 0) \ |
| res += 5; \ |
| return res; \ |
| } |
| |
| #define VALUETEST(func) \ |
| static int __attribute__((noinline)) \ |
| func ## _f (double d) \ |
| { \ |
| return func (d); \ |
| } \ |
| static int \ |
| func ## _t (volatile double *p, size_t n, size_t iters) \ |
| { \ |
| int i, j; \ |
| int res = 0; \ |
| for (j = 0; j < iters; j++) \ |
| for (i = 0; i < n; i++) \ |
| res += func ## _f (p[i] * 2.0); \ |
| return res; \ |
| } |
| |
| typedef union |
| { |
| double value; |
| uint64_t word; |
| } ieee_double_shape_type; |
| |
| #define EXTRACT_WORDS64(i,d) \ |
| do { \ |
| ieee_double_shape_type gh_u; \ |
| gh_u.value = (d); \ |
| (i) = gh_u.word; \ |
| } while (0) |
| |
| /* Inlines similar to existing math_private.h versions. */ |
| |
| static __always_inline int |
| __isnan_inl (double d) |
| { |
| uint64_t di; |
| EXTRACT_WORDS64 (di, d); |
| return (di & 0x7fffffffffffffffull) > 0x7ff0000000000000ull; |
| } |
| |
| static __always_inline int |
| __isinf_ns2 (double d) |
| { |
| uint64_t di; |
| EXTRACT_WORDS64 (di, d); |
| return (di & 0x7fffffffffffffffull) == 0x7ff0000000000000ull; |
| } |
| |
| static __always_inline int |
| __finite_inl (double d) |
| { |
| uint64_t di; |
| EXTRACT_WORDS64 (di, d); |
| return (di & 0x7fffffffffffffffull) < 0x7ff0000000000000ull; |
| } |
| |
| #define __isnormal_inl(X) (__fpclassify (X) == FP_NORMAL) |
| |
| /* Inlines for the builtin functions. */ |
| |
| #define __isnan_builtin(X) __builtin_isnan (X) |
| #define __isinf_ns_builtin(X) __builtin_isinf (X) |
| #define __isinf_builtin(X) __builtin_isinf_sign (X) |
| #define __isfinite_builtin(X) __builtin_isfinite (X) |
| #define __isnormal_builtin(X) __builtin_isnormal (X) |
| #define __fpclassify_builtin(X) __builtin_fpclassify (FP_NAN, FP_INFINITE, \ |
| FP_NORMAL, FP_SUBNORMAL, FP_ZERO, (X)) |
| |
| static double __attribute ((noinline)) |
| kernel_standard (double x, double y, int z) |
| { |
| return x * y + z; |
| } |
| |
| volatile double rem1 = 2.5; |
| |
| static __always_inline double |
| remainder_test1 (double x) |
| { |
| double y = rem1; |
| if (((__builtin_expect (y == 0.0, 0) && !__isnan_inl (x)) |
| || (__builtin_expect (__isinf_ns2 (x), 0) && !__isnan_inl (y)))) |
| return kernel_standard (x, y, 10); |
| |
| return remainder (x, y); |
| } |
| |
| static __always_inline double |
| remainder_test2 (double x) |
| { |
| double y = rem1; |
| if (((__builtin_expect (y == 0.0, 0) && !__builtin_isnan (x)) |
| || (__builtin_expect (__builtin_isinf (x), 0) && !__builtin_isnan (y)))) |
| return kernel_standard (x, y, 10); |
| |
| return remainder (x, y); |
| } |
| |
| /* Create test functions for each possibility. */ |
| |
| BOOLTEST (__isnan) |
| BOOLTEST (__isnan_inl) |
| BOOLTEST (__isnan_builtin) |
| BOOLTEST (isnan) |
| |
| BOOLTEST (__isinf) |
| BOOLTEST (__isinf_builtin) |
| BOOLTEST (__isinf_ns2) |
| BOOLTEST (__isinf_ns_builtin) |
| BOOLTEST (isinf) |
| |
| BOOLTEST (__finite) |
| BOOLTEST (__finite_inl) |
| BOOLTEST (__isfinite_builtin) |
| BOOLTEST (isfinite) |
| |
| BOOLTEST (__isnormal_inl) |
| BOOLTEST (__isnormal_builtin) |
| BOOLTEST (isnormal) |
| |
| VALUETEST (__fpclassify) |
| VALUETEST (__fpclassify_builtin) |
| VALUETEST (fpclassify) |
| |
| VALUETEST (remainder_test1) |
| VALUETEST (remainder_test2) |
| |
| typedef int (*proto_t) (volatile double *p, size_t n, size_t iters); |
| |
| typedef struct |
| { |
| const char *name; |
| proto_t fn; |
| } impl_t; |
| |
| #define IMPL(name) { #name, name ## _t } |
| |
| static impl_t test_list[] = |
| { |
| IMPL (__isnan), |
| IMPL (__isnan_inl), |
| IMPL (__isnan_builtin), |
| IMPL (isnan), |
| |
| IMPL (__isinf), |
| IMPL (__isinf_ns2), |
| IMPL (__isinf_ns_builtin), |
| IMPL (__isinf_builtin), |
| IMPL (isinf), |
| |
| IMPL (__finite), |
| IMPL (__finite_inl), |
| IMPL (__isfinite_builtin), |
| IMPL (isfinite), |
| |
| IMPL (__isnormal_inl), |
| IMPL (__isnormal_builtin), |
| IMPL (isnormal), |
| |
| IMPL (__fpclassify), |
| IMPL (__fpclassify_builtin), |
| IMPL (fpclassify), |
| |
| IMPL (remainder_test1), |
| IMPL (remainder_test2) |
| }; |
| |
| static void |
| do_one_test (json_ctx_t *json_ctx, proto_t test_fn, volatile double *arr, |
| size_t len, const char *testname) |
| { |
| size_t iters = 500; |
| timing_t start, stop, cur; |
| |
| json_attr_object_begin (json_ctx, testname); |
| |
| TIMING_NOW (start); |
| test_fn (arr, len, iters); |
| TIMING_NOW (stop); |
| TIMING_DIFF (cur, start, stop); |
| |
| json_attr_double (json_ctx, "duration", cur); |
| json_attr_double (json_ctx, "iterations", iters); |
| json_attr_double (json_ctx, "mean", cur / iters); |
| json_attr_object_end (json_ctx); |
| } |
| |
| static volatile double arr1[SIZE]; |
| static volatile double arr2[SIZE]; |
| |
| int |
| test_main (void) |
| { |
| json_ctx_t json_ctx; |
| size_t i; |
| |
| bench_start (); |
| |
| json_init (&json_ctx, 2, stdout); |
| json_attr_object_begin (&json_ctx, TEST_NAME); |
| |
| /* Create 2 test arrays, one with 10% zeroes, 10% negative values, |
| 79% positive values and 1% infinity/NaN. The other contains |
| 50% inf, 50% NaN. This relies on rand behaving correctly. */ |
| |
| for (i = 0; i < SIZE; i++) |
| { |
| int x = rand () & 255; |
| arr1[i] = (x < 25) ? 0.0 : ((x < 50) ? -1 : 100); |
| if (x == 255) arr1[i] = __builtin_inf (); |
| if (x == 254) arr1[i] = __builtin_nan ("0"); |
| arr2[i] = (x < 128) ? __builtin_inf () : __builtin_nan ("0"); |
| } |
| |
| for (i = 0; i < sizeof (test_list) / sizeof (test_list[0]); i++) |
| { |
| json_attr_object_begin (&json_ctx, test_list[i].name); |
| do_one_test (&json_ctx, test_list[i].fn, arr2, SIZE, "inf/nan"); |
| json_attr_object_end (&json_ctx); |
| } |
| |
| for (i = 0; i < sizeof (test_list) / sizeof (test_list[0]); i++) |
| { |
| json_attr_object_begin (&json_ctx, test_list[i].name); |
| do_one_test (&json_ctx, test_list[i].fn, arr1, SIZE, "normal"); |
| json_attr_object_end (&json_ctx); |
| } |
| |
| json_attr_object_end (&json_ctx); |
| return 0; |
| } |
| |
| #include "bench-util.c" |
| #include "../test-skeleton.c" |