/* [Description] Copyright 2010-2020 Free Software Foundation, Inc. Contributed by the AriC and Caramba projects, INRIA. This file is part of the GNU MPFR Library. The GNU MPFR 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 3 of the License, or (at your option) any later version. The GNU MPFR 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 MPFR Library; see the file COPYING.LESSER. If not, see https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <stdlib.h> #include <time.h> #define MPFR_NEED_LONGLONG_H #include "mpfr-impl.h" #undef _PROTO #define _PROTO __GMP_PROTO #include "speed.h" /* Let f be a function for which we have several implementations f1, f2... */ /* We wish to have a quick overview of which implementation is the best */ /* in function of the point x where f(x) is computed and of the precision */ /* prec requested by the user. */ /* This is performed by drawing a 2D graphic with color indicating which */ /* method is the best. */ /* For building this graphic, the following structur is used (see the core */ /* of generate_2D_sample for an explanation of each field. */ struct speed_params2D { /* x-window: [min_x, max_x] or [2^min_x, 2^max_x] */ /* or [-2^(max_x), -2^(min_x)] U [2^min_x, 2^max_x] */ /* depending on the value of logarithmic_scale_x */ double min_x; double max_x; /* prec-window: [min_prec, max_prec] */ mpfr_prec_t min_prec; mpfr_prec_t max_prec; /* number of sample points for the x-axis and the prec-axis */ int nb_points_x; int nb_points_prec; /* should the sample points be logarithmically scaled or not */ int logarithmic_scale_x; int logarithmic_scale_prec; /* list of functions g1, g2... measuring the execution time of f1, f2... */ /* when given a point x and a precision prec stored in s. */ /* We use s->xp to store the significant of x, s->r to store its exponent */ /* s->align_xp to store its sign, and s->size to store prec. */ double (**speed_funcs) (struct speed_params *s); }; /* Given an array t of nb_functions double indicating the timings of several */ /* implementations, return i, such that t[i] is the best timing. */ int find_best (double *t, int nb_functions) { int i, ibest; double best; if (nb_functions<=0) { fprintf (stderr, "There is no function\n"); abort (); } ibest = 0; best = t[0]; for (i=1; i<nb_functions; i++) { if (t[i]<best) { best = t[i]; ibest = i; } } return ibest; } void generate_2D_sample (FILE *output, struct speed_params2D param) { mpfr_t temp; double incr_prec; mpfr_t incr_x; mpfr_t x, x2; double prec; struct speed_params s; int i; int test; int nb_functions; double *t; /* store the timing of each implementation */ /* We first determine how many implementations we have */ nb_functions = 0; while (param.speed_funcs[nb_functions] != NULL) nb_functions++; t = malloc (nb_functions * sizeof (double)); if (t == NULL) { fprintf (stderr, "Can't allocate memory.\n"); abort (); } mpfr_init2 (temp, MPFR_SMALL_PRECISION); /* The precision is sampled from min_prec to max_prec with */ /* approximately nb_points_prec points. If logarithmic_scale_prec */ /* is true, the precision is multiplied by incr_prec at each */ /* step. Otherwise, incr_prec is added at each step. */ if (param.logarithmic_scale_prec) { mpfr_set_ui (temp, (unsigned long int)param.max_prec, MPFR_RNDU); mpfr_div_ui (temp, temp, (unsigned long int)param.min_prec, MPFR_RNDU); mpfr_root (temp, temp, (unsigned long int)param.nb_points_prec, MPFR_RNDU); incr_prec = mpfr_get_d (temp, MPFR_RNDU); } else { incr_prec = (double)param.max_prec - (double)param.min_prec; incr_prec = incr_prec/((double)param.nb_points_prec); } /* The points x are sampled according to the following rule: */ /* If logarithmic_scale_x = 0: */ /* nb_points_x points are equally distributed between min_x and max_x */ /* If logarithmic_scale_x = 1: */ /* nb_points_x points are sampled from 2^(min_x) to 2^(max_x). At */ /* each step, the current point is multiplied by incr_x. */ /* If logarithmic_scale_x = -1: */ /* nb_points_x/2 points are sampled from -2^(max_x) to -2^(min_x) */ /* (at each step, the current point is divided by incr_x); and */ /* nb_points_x/2 points are sampled from 2^(min_x) to 2^(max_x) */ /* (at each step, the current point is multiplied by incr_x). */ mpfr_init2 (incr_x, param.max_prec); if (param.logarithmic_scale_x == 0) { mpfr_set_d (incr_x, (param.max_x - param.min_x)/(double)param.nb_points_x, MPFR_RNDU); } else if (param.logarithmic_scale_x == -1) { mpfr_set_d (incr_x, 2.*(param.max_x - param.min_x)/(double)param.nb_points_x, MPFR_RNDU); mpfr_exp2 (incr_x, incr_x, MPFR_RNDU); } else { /* other values of param.logarithmic_scale_x are considered as 1 */ mpfr_set_d (incr_x, (param.max_x - param.min_x)/(double)param.nb_points_x, MPFR_RNDU); mpfr_exp2 (incr_x, incr_x, MPFR_RNDU); } /* Main loop */ mpfr_init2 (x, param.max_prec); mpfr_init2 (x2, param.max_prec); prec = (double)param.min_prec; while (prec <= param.max_prec) { printf ("prec = %d\n", (int)prec); if (param.logarithmic_scale_x == 0) mpfr_set_d (temp, param.min_x, MPFR_RNDU); else if (param.logarithmic_scale_x == -1) { mpfr_set_d (temp, param.max_x, MPFR_RNDD); mpfr_exp2 (temp, temp, MPFR_RNDD); mpfr_neg (temp, temp, MPFR_RNDU); } else { mpfr_set_d (temp, param.min_x, MPFR_RNDD); mpfr_exp2 (temp, temp, MPFR_RNDD); } /* We perturb x a little bit, in order to avoid trailing zeros that */ /* might change the behavior of algorithms. */ mpfr_const_pi (x, MPFR_RNDN); mpfr_div_2ui (x, x, 7, MPFR_RNDN); mpfr_add_ui (x, x, 1, MPFR_RNDN); mpfr_mul (x, x, temp, MPFR_RNDN); test = 1; while (test) { mpfr_fprintf (output, "%e\t", mpfr_get_d (x, MPFR_RNDN)); mpfr_fprintf (output, "%Pu\t", (mpfr_prec_t)prec); s.r = (mp_limb_t)mpfr_get_exp (x); s.size = (mpfr_prec_t)prec; s.align_xp = (mpfr_sgn (x) > 0)?1:2; mpfr_set_prec (x2, (mpfr_prec_t)prec); mpfr_set (x2, x, MPFR_RNDU); s.xp = x2->_mpfr_d; for (i=0; i<nb_functions; i++) { t[i] = speed_measure (param.speed_funcs[i], &s); mpfr_fprintf (output, "%e\t", t[i]); } fprintf (output, "%d\n", 1 + find_best (t, nb_functions)); if (param.logarithmic_scale_x == 0) { mpfr_add (x, x, incr_x, MPFR_RNDU); if (mpfr_cmp_d (x, param.max_x) > 0) test=0; } else { if (mpfr_sgn (x) < 0 ) { /* if x<0, it means that logarithmic_scale_x=-1 */ mpfr_div (x, x, incr_x, MPFR_RNDU); mpfr_abs (temp, x, MPFR_RNDD); mpfr_log2 (temp, temp, MPFR_RNDD); if (mpfr_cmp_d (temp, param.min_x) < 0) mpfr_neg (x, x, MPFR_RNDN); } else { mpfr_mul (x, x, incr_x, MPFR_RNDU); mpfr_set (temp, x, MPFR_RNDD); mpfr_log2 (temp, temp, MPFR_RNDD); if (mpfr_cmp_d (temp, param.max_x) > 0) test=0; } } } prec = ( (param.logarithmic_scale_prec) ? (prec * incr_prec) : (prec + incr_prec) ); fprintf (output, "\n"); } free (t); mpfr_clear (incr_x); mpfr_clear (x); mpfr_clear (x2); mpfr_clear (temp); return; } #define SPEED_MPFR_FUNC_2D(mean_func) \ do \ { \ double t; \ unsigned i; \ mpfr_t w, x; \ mp_size_t size; \ \ SPEED_RESTRICT_COND (s->size >= MPFR_PREC_MIN); \ SPEED_RESTRICT_COND (s->size <= MPFR_PREC_MAX); \ \ size = (s->size-1)/GMP_NUMB_BITS+1; \ s->xp[size-1] |= MPFR_LIMB_HIGHBIT; \ MPFR_TMP_INIT1 (s->xp, x, s->size); \ MPFR_SET_EXP (x, (mpfr_exp_t) s->r); \ if (s->align_xp == 2) MPFR_SET_NEG (x); \ \ mpfr_init2 (w, s->size); \ speed_starttime (); \ i = s->reps; \ \ do \ mean_func (w, x, MPFR_RNDN); \ while (--i != 0); \ t = speed_endtime (); \ \ mpfr_clear (w); \ return t; \ } \ while (0) mpfr_prec_t mpfr_exp_2_threshold; mpfr_prec_t old_threshold = MPFR_EXP_2_THRESHOLD; #undef MPFR_EXP_2_THRESHOLD #define MPFR_EXP_2_THRESHOLD mpfr_exp_2_threshold #include "exp_2.c" double timing_exp1 (struct speed_params *s) { mpfr_exp_2_threshold = s->size+1; SPEED_MPFR_FUNC_2D (mpfr_exp_2); } double timing_exp2 (struct speed_params *s) { mpfr_exp_2_threshold = s->size-1; SPEED_MPFR_FUNC_2D (mpfr_exp_2); } double timing_exp3 (struct speed_params *s) { SPEED_MPFR_FUNC_2D (mpfr_exp_3); } #include "ai.c" double timing_ai1 (struct speed_params *s) { SPEED_MPFR_FUNC_2D (mpfr_ai1); } double timing_ai2 (struct speed_params *s) { SPEED_MPFR_FUNC_2D (mpfr_ai2); } /* These functions are for testing purpose only */ /* They are used to draw which method is actually used */ double virtual_timing_ai1 (struct speed_params *s) { double t; unsigned i; mpfr_t w, x; mp_size_t size; mpfr_t temp1, temp2; SPEED_RESTRICT_COND (s->size >= MPFR_PREC_MIN); SPEED_RESTRICT_COND (s->size <= MPFR_PREC_MAX); size = (s->size-1)/GMP_NUMB_BITS+1; s->xp[size-1] |= MPFR_LIMB_HIGHBIT; MPFR_TMP_INIT1 (s->xp, x, s->size); MPFR_SET_EXP (x, (mpfr_exp_t) s->r); if (s->align_xp == 2) MPFR_SET_NEG (x); mpfr_init2 (w, s->size); speed_starttime (); i = s->reps; mpfr_init2 (temp1, MPFR_SMALL_PRECISION); mpfr_init2 (temp2, MPFR_SMALL_PRECISION); mpfr_set (temp1, x, MPFR_SMALL_PRECISION); mpfr_set_si (temp2, MPFR_AI_THRESHOLD2, MPFR_RNDN); mpfr_mul_ui (temp2, temp2, (unsigned int)MPFR_PREC (w), MPFR_RNDN); if (MPFR_IS_NEG (x)) mpfr_mul_si (temp1, temp1, MPFR_AI_THRESHOLD1, MPFR_RNDN); else mpfr_mul_si (temp1, temp1, MPFR_AI_THRESHOLD3, MPFR_RNDN); mpfr_add (temp1, temp1, temp2, MPFR_RNDN); if (mpfr_cmp_si (temp1, MPFR_AI_SCALE) > 0) t = 1000.; else t = 1.; mpfr_clear (temp1); mpfr_clear (temp2); return t; } double virtual_timing_ai2 (struct speed_params *s) { double t; unsigned i; mpfr_t w, x; mp_size_t size; mpfr_t temp1, temp2; SPEED_RESTRICT_COND (s->size >= MPFR_PREC_MIN); SPEED_RESTRICT_COND (s->size <= MPFR_PREC_MAX); size = (s->size-1)/GMP_NUMB_BITS+1; s->xp[size-1] |= MPFR_LIMB_HIGHBIT; MPFR_TMP_INIT1 (s->xp, x, s->size); MPFR_SET_EXP (x, (mpfr_exp_t) s->r); if (s->align_xp == 2) MPFR_SET_NEG (x); mpfr_init2 (w, s->size); speed_starttime (); i = s->reps; mpfr_init2 (temp1, MPFR_SMALL_PRECISION); mpfr_init2 (temp2, MPFR_SMALL_PRECISION); mpfr_set (temp1, x, MPFR_SMALL_PRECISION); mpfr_set_si (temp2, MPFR_AI_THRESHOLD2, MPFR_RNDN); mpfr_mul_ui (temp2, temp2, (unsigned int)MPFR_PREC (w), MPFR_RNDN); if (MPFR_IS_NEG (x)) mpfr_mul_si (temp1, temp1, MPFR_AI_THRESHOLD1, MPFR_RNDN); else mpfr_mul_si (temp1, temp1, MPFR_AI_THRESHOLD3, MPFR_RNDN); mpfr_add (temp1, temp1, temp2, MPFR_RNDN); if (mpfr_cmp_si (temp1, MPFR_AI_SCALE) > 0) t = 1.; else t = 1000.; mpfr_clear (temp1); mpfr_clear (temp2); return t; } int main (void) { FILE *output; struct speed_params2D param; double (*speed_funcs[3]) (struct speed_params *s); /* char filename[256] = "virtual_timing_ai.dat"; */ /* speed_funcs[0] = virtual_timing_ai1; */ /* speed_funcs[1] = virtual_timing_ai2; */ char filename[256] = "airy.dat"; speed_funcs[0] = timing_ai1; speed_funcs[1] = timing_ai2; speed_funcs[2] = NULL; output = fopen (filename, "w"); if (output == NULL) { fprintf (stderr, "Can't open file '%s' for writing.\n", filename); abort (); } param.min_x = -80; param.max_x = 60; param.min_prec = 50; param.max_prec = 1500; param.nb_points_x = 200; param.nb_points_prec = 200; param.logarithmic_scale_x = 0; param.logarithmic_scale_prec = 0; param.speed_funcs = speed_funcs; generate_2D_sample (output, param); fclose (output); mpfr_free_cache (); return 0; }