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