1/* mpfrbench.c -- compute the timings for the MPFRbench benchmark 2 3Copyright 1999, 2001-2023 Free Software Foundation, Inc. 4Contributed by the AriC and Caramba projects, INRIA. 5 6This file is part of the GNU MPFR Library. 7 8The GNU MPFR Library is free software; you can redistribute it and/or modify 9it under the terms of the GNU Lesser General Public License as published by 10the Free Software Foundation; either version 3 of the License, or (at your 11option) any later version. 12 13The GNU MPFR Library is distributed in the hope that it will be useful, but 14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 16License for more details. 17 18You should have received a copy of the GNU Lesser General Public License 19along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see 20https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23#include <stdlib.h> 24#include <stdio.h> 25#ifdef HAVE_GETRUSAGE 26#include <sys/time.h> 27#include <sys/resource.h> 28#else 29#include <time.h> 30#endif 31#include "mpfr.h" 32#include "benchtime.h" 33 34static unsigned long get_cputime (void); 35 36/* enumeration of the group of functions */ 37enum egroupfunc 38{ 39 egroup_arith = 0, /* e.g., arith ... */ 40 egroup_special, /* e.g., cos, ... */ 41 egroup_last /* to get the number of enum */ 42}; 43 44/* name of the group of functions */ 45const char *groupname [] = { 46 "Arith ", 47 "Special" 48}; 49 50 51 52struct benchfunc 53{ 54 const char *name; /* name of the function */ 55 double (*func_init) (int n, mpfr_t * z, mpfr_t * x, mpfr_t * y); /* compute the time for one call (not accurate) */ 56 unsigned long int (*func_accurate) (unsigned long int niter, int n, mpfr_t * z, mpfr_t * x, mpfr_t * y, int nop); /* compute the time for "niter" calls (accurate) */ 57 enum egroupfunc group; /* group of the function */ 58 int noperands; /* number of operands */ 59}; 60 61 62/* declare the function to compute the cost for one call of the function */ 63DECLARE_TIME_2OP (mpfr_mul) 64DECLARE_TIME_2OP (mpfr_add) 65DECLARE_TIME_2OP (mpfr_sub) 66DECLARE_TIME_2OP (mpfr_div) 67DECLARE_TIME_1OP (mpfr_sqrt) 68DECLARE_TIME_1OP (mpfr_exp) 69DECLARE_TIME_1OP (mpfr_log) 70DECLARE_TIME_1OP (mpfr_sin) 71DECLARE_TIME_1OP (mpfr_cos) 72DECLARE_TIME_1OP (mpfr_asin) 73DECLARE_TIME_1OP (mpfr_acos) 74 75/* number of operations to score */ 76#define NB_BENCH_OP 11 77/* number of random numbers */ 78#define NB_RAND_FLOAT 10000 79 80/* list of functions to compute the score */ 81const struct benchfunc arrayfunc[NB_BENCH_OP] = { 82 {"mul", ADDR_TIME_NOP (mpfr_mul), ADDR_ACCURATE_TIME_NOP (mpfr_mul), egroup_arith, 2}, 83 {"add", ADDR_TIME_NOP (mpfr_add), ADDR_ACCURATE_TIME_NOP (mpfr_add), egroup_arith, 2}, 84 {"sub", ADDR_TIME_NOP (mpfr_sub), ADDR_ACCURATE_TIME_NOP (mpfr_sub), egroup_arith, 2}, 85 {"div", ADDR_TIME_NOP (mpfr_div), ADDR_ACCURATE_TIME_NOP (mpfr_div), egroup_arith, 2}, 86 {"sqrt", ADDR_TIME_NOP (mpfr_sqrt), ADDR_ACCURATE_TIME_NOP (mpfr_sqrt), egroup_special, 1}, 87 {"exp", ADDR_TIME_NOP (mpfr_exp), ADDR_ACCURATE_TIME_NOP (mpfr_exp), egroup_special, 1}, 88 {"log", ADDR_TIME_NOP (mpfr_log), ADDR_ACCURATE_TIME_NOP (mpfr_log), egroup_special, 1}, 89 {"cos", ADDR_TIME_NOP (mpfr_cos), ADDR_ACCURATE_TIME_NOP (mpfr_cos), egroup_special, 1}, 90 {"sin", ADDR_TIME_NOP (mpfr_sin), ADDR_ACCURATE_TIME_NOP (mpfr_sin), egroup_special, 1}, 91 {"acos", ADDR_TIME_NOP (mpfr_acos), ADDR_ACCURATE_TIME_NOP (mpfr_acos), egroup_special, 1}, 92 {"asin", ADDR_TIME_NOP (mpfr_asin), ADDR_ACCURATE_TIME_NOP (mpfr_asin), egroup_special, 1} 93}; 94 95/* the following arrays must have the same number of elements */ 96 97/* list of precisions to test for the first operand */ 98const int arrayprecision_op1[] = 99 { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 100 50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 1500, 3000, 5000, 101 }; 102 103/* list of precisions to test for the second operand */ 104const int arrayprecision_op2[] = 105 { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 106 50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 3000, 6000, 10000 107 }; 108 109/* get the time in microseconds */ 110static unsigned long 111get_cputime (void) 112{ 113#ifdef HAVE_GETRUSAGE 114 struct rusage ru; 115 116 getrusage (RUSAGE_SELF, &ru); 117 return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec 118 + ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec; 119#else 120 return (unsigned long) ((double) clock () / ((double) CLOCKS_PER_SEC / 1e6)); 121#endif 122} 123 124/* initialize an array of n random numbers */ 125static mpfr_t * 126bench_random_array (int n, mpfr_prec_t precision, gmp_randstate_t randstate) 127{ 128 int j; 129 mpfr_t *ptr; 130 131 ptr = (mpfr_t *) malloc (n * sizeof (mpfr_t)); 132 if (ptr == NULL) 133 { 134 printf ("Can't allocate memory for %d numbers\n", n); 135 exit (1); 136 return NULL; 137 } 138 for (j = 0; j < n; j++) 139 { 140 mpfr_init2 (ptr[j], precision); 141 mpfr_urandomb (ptr[j], randstate); 142 } 143 return ptr; 144} 145 146/* compute the score for the operation arrayfunc[op] */ 147static void 148compute_score (mpz_t zscore, int op, gmp_randstate_t randstate) 149{ 150 mpfr_t *xptr, *yptr, *zptr; 151 int i, j; 152 size_t k; 153 unsigned long niter, ti; 154 double t; 155 unsigned long ops_per_sec; 156 int countprec = 0; 157 158 mpz_init_set_si (zscore, 1); 159 160 i = op; 161 for (k = 0; k < (int) sizeof (arrayprecision_op1) / sizeof (arrayprecision_op1[0]); 162 k++, countprec++) 163 { 164 mpfr_prec_t precision1 = arrayprecision_op1[k]; 165 mpfr_prec_t precision2 = arrayprecision_op2[k]; 166 mpfr_prec_t precision3 = arrayprecision_op2[k]; 167 168 /* allocate array of random numbers */ 169 xptr = bench_random_array (NB_RAND_FLOAT, precision1, randstate); 170 yptr = bench_random_array (NB_RAND_FLOAT, precision2, randstate); 171 zptr = bench_random_array (NB_RAND_FLOAT, precision3, randstate); 172 173 /* compute the number of operations per second */ 174 if (arrayfunc[i].noperands==2) 175 { 176 printf ("operation %5s, precision : %5lux%5lu to %5lu bits ... ", arrayfunc[i].name, precision1, precision2, precision3); 177 } 178 else 179 { 180 printf ("operation %5s, precision : %5lu to %5lu bits ... ", arrayfunc[i].name, precision1, precision3); 181 } 182 fflush (stdout); 183 184 t = arrayfunc[i].func_init (NB_RAND_FLOAT, zptr, xptr, yptr); 185 niter = 1 + (unsigned long) (1e6 / t); 186 187 printf (" %10lu iterations ...", niter); 188 fflush (stdout); 189 190 /* ti expressed in microseconds */ 191 ti = arrayfunc[i].func_accurate (niter, NB_RAND_FLOAT, zptr, xptr, yptr, arrayfunc[i].noperands); 192 193 ops_per_sec = (unsigned long) (1000000E0 * niter / (double) ti); 194 195 printf (" %10lu operations per second\n", ops_per_sec); 196 197 mpz_mul_ui (zscore, zscore, ops_per_sec); 198 199 /* free memory */ 200 for (j = 0; j < NB_RAND_FLOAT; j++) 201 { 202 mpfr_clear (xptr[j]); 203 mpfr_clear (yptr[j]); 204 mpfr_clear (zptr[j]); 205 } 206 free (xptr); 207 free (yptr); 208 free (zptr); 209 } 210 211 mpz_root (zscore, zscore, countprec); 212} 213 214/* compute the score for all groups */ 215static void 216compute_groupscore (mpz_t groupscore[], int countop, mpz_t zscore[]) 217{ 218 int op; 219 enum egroupfunc group; 220 int countgroupop; 221 222 for (group = (enum egroupfunc)0; group != egroup_last; group++) 223 { 224 mpz_init_set_si (groupscore[group], 1); 225 for (op = 0, countgroupop = 0; op < countop; op++) 226 { 227 if (group == arrayfunc[op].group) 228 { 229 mpz_mul (groupscore[group], groupscore[group], zscore[op]); 230 countgroupop++; 231 } 232 } 233 mpz_root (groupscore[group], groupscore[group], countgroupop); 234 } 235} 236 237 238/* compute the global score */ 239static void 240compute_globalscore (mpz_t globalscore, int countop, mpz_t zscore[]) 241{ 242 int op; 243 244 mpz_init_set_si (globalscore, 1); 245 for (op = 0; op < countop; op++) 246 { 247 mpz_mul (globalscore, globalscore, zscore[op]); 248 } 249 mpz_root (globalscore, globalscore, countop); 250} 251 252int 253main (void) 254{ 255 int i; 256 enum egroupfunc group; 257 mpz_t score[NB_BENCH_OP]; 258 mpz_t globalscore, groupscore[egroup_last]; 259 gmp_randstate_t randstate; 260 261 gmp_randinit_default (randstate); 262 263 for (i = 0; i < NB_BENCH_OP; i++) 264 { 265 compute_score (score[i], i, randstate); 266 } 267 compute_globalscore (globalscore, NB_BENCH_OP, score); 268 compute_groupscore (groupscore, NB_BENCH_OP, score); 269 270 printf ("\n=================================================================\n\n"); 271 printf ("GMP : %s MPFR : %s \n", gmp_version, mpfr_get_version ()); 272#ifdef __GMP_CC 273 printf ("GMP compiler : %s\n", __GMP_CC); 274#endif 275#ifdef __GMP_CFLAGS 276 printf ("GMP flags : %s\n", __GMP_CFLAGS); 277#endif 278 printf ("\n\n"); 279 280 for (i = 0; i < NB_BENCH_OP; i++) 281 { 282 gmp_printf ("\tscore for %5s : %12Zd\n", arrayfunc[i].name, score[i]); 283 if (i == NB_BENCH_OP-1 || arrayfunc[i+1].group != arrayfunc[i].group) 284 { 285 enum egroupfunc g = arrayfunc[i].group; 286 gmp_printf ("group score %s : %12Zd\n\n", groupname[g], groupscore[g]); 287 } 288 } 289 /* divide by 132 the global score to get about 10^3 on a 290 Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz 291 with GMP : 5.1.3 MPFR : 3.1.2 292 GMP compiler: gcc -std=gnu99, GMP flags: -O2 -pedantic 293 -fomit-frame-pointer -m64 -mtune=core2 -march=core2 */ 294 mpz_div_ui (globalscore, globalscore, 132); 295 gmp_printf ("global score : %12Zd\n\n", globalscore); 296 297 for (i = 0; i < NB_BENCH_OP; i++) 298 { 299 mpz_clear (score[i]); 300 } 301 302 for (group = (enum egroupfunc)0; group != egroup_last; group++) 303 { 304 mpz_clear (groupscore[group]); 305 } 306 mpz_clear (globalscore); 307 gmp_randclear (randstate); 308 return 0; 309} 310