1/* mpcbench.c -- perform the benchmark on the complex numbers.
2
3Copyright (C) 2014 CNRS - INRIA
4
5This file is part of GNU MPC.
6
7GNU MPC is free software; you can redistribute it and/or modify it under
8the terms of the GNU Lesser General Public License as published by the
9Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
11
12GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15more details.
16
17You should have received a copy of the GNU Lesser General Public License
18along with this program. If not, see http://www.gnu.org/licenses/ .
19*/
20
21#include "config.h"
22#include <stdlib.h>
23#include <stdio.h>
24#include <math.h>
25#ifdef HAVE_SYS_TIME_H
26#include <sys/time.h>
27#endif
28#ifdef HAVE_SYS_RESOURCE_H
29#include <sys/resource.h>
30#endif
31#include "mpc.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, mpc_t * z, mpc_t * x, mpc_t * y); /* compute the time for one call (not accurate) */
56  unsigned long int (*func_accurate) (unsigned long int niter, int n, mpc_t * z, mpc_t * x, mpc_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 mpc function */
63DECLARE_TIME_2OP (mpc_add)
64DECLARE_TIME_2OP (mpc_sub)
65DECLARE_TIME_2OP (mpc_mul)
66DECLARE_TIME_2OP (mpc_div)
67DECLARE_TIME_1OP (mpc_sqrt)
68DECLARE_TIME_1OP (mpc_exp)
69DECLARE_TIME_1OP (mpc_log)
70DECLARE_TIME_2OP (mpc_pow)
71DECLARE_TIME_1OP (mpc_sin)
72DECLARE_TIME_1OP (mpc_cos)
73DECLARE_TIME_1OP (mpc_asin)
74DECLARE_TIME_1OP (mpc_acos)
75
76/* number of operations to score*/
77#define NB_BENCH_OP 12
78/* number of random numbers */
79#define NB_RAND_CPLX 10000
80
81/* list of functions to compute the score */
82const struct benchfunc
83      arrayfunc[NB_BENCH_OP] = {
84      {"add", ADDR_TIME_NOP (mpc_add), ADDR_ACCURATE_TIME_NOP (mpc_add), egroup_arith, 2},
85      {"sub", ADDR_TIME_NOP (mpc_sub), ADDR_ACCURATE_TIME_NOP (mpc_sub), egroup_arith, 2},
86      {"mul", ADDR_TIME_NOP (mpc_mul), ADDR_ACCURATE_TIME_NOP (mpc_mul), egroup_arith, 2},
87      {"div", ADDR_TIME_NOP (mpc_div), ADDR_ACCURATE_TIME_NOP (mpc_div), egroup_arith, 2},
88      {"sqrt", ADDR_TIME_NOP (mpc_sqrt), ADDR_ACCURATE_TIME_NOP (mpc_sqrt), egroup_arith, 1},
89      {"exp", ADDR_TIME_NOP (mpc_exp), ADDR_ACCURATE_TIME_NOP (mpc_exp), egroup_special, 1},
90      {"log", ADDR_TIME_NOP (mpc_log), ADDR_ACCURATE_TIME_NOP (mpc_log), egroup_special, 1},
91      {"pow", ADDR_TIME_NOP (mpc_pow), ADDR_ACCURATE_TIME_NOP (mpc_pow), egroup_special, 2},
92      {"sin", ADDR_TIME_NOP (mpc_sin), ADDR_ACCURATE_TIME_NOP (mpc_sin), egroup_special, 1},
93      {"cos", ADDR_TIME_NOP (mpc_cos), ADDR_ACCURATE_TIME_NOP (mpc_cos), egroup_special, 1},
94      {"asin", ADDR_TIME_NOP (mpc_asin), ADDR_ACCURATE_TIME_NOP (mpc_asin), egroup_special, 1},
95      {"acos", ADDR_TIME_NOP (mpc_acos), ADDR_ACCURATE_TIME_NOP (mpc_acos), egroup_special, 1}
96    };
97
98/* the following arrays must have the same number of elements */
99
100/* list of precisions to test for the first operand */
101const int arrayprecision_op1[] =
102  { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
103  50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 1500, 3000, 5000,
104};
105
106/* list of precisions to test for the second operand */
107const int arrayprecision_op2[] =
108  { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
109  50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 3000, 6000, 10000
110
111};
112
113/* get the time in microseconds */
114static unsigned long
115get_cputime (void)
116{
117#ifdef HAVE_GETRUSAGE
118  struct rusage ru;
119
120  getrusage (RUSAGE_SELF, &ru);
121  return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec
122         +ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec;
123#else
124  printf("\nthe function getrusage not available\n");
125  exit(1);
126  return 0;
127#endif
128}
129
130/* initialize an array of n random complex numbers */
131static mpc_t *
132bench_random_array (int n, mpfr_prec_t precision, gmp_randstate_t randstate)
133{
134  int j;
135
136  mpc_t *ptr;
137
138  ptr = (mpc_t *) malloc (n * sizeof (mpc_t));
139  if (ptr == NULL)
140    {
141      printf ("Can't allocate memory for %d complex numbers\n", n);
142      exit (1);
143      return NULL;
144    }
145  for (j = 0; j < n; j++)
146    {
147      mpc_init2 (ptr[j], precision);
148      mpc_urandom (ptr[j], randstate);
149    }
150  return ptr;
151}
152
153
154/* Print the positive number x with 3 significant digits or at most 3 digits
155   after the komma, using 7 digits before the komma. */
156static void sensible_print (double x)
157{
158   if (x < 1)
159      printf ("%11.3f", x);
160   else if (x < 10)
161      printf ("%10.2f", x);
162   else if (x < 100)
163      printf ("%9.1f", x);
164   else {
165      unsigned long int r;
166      unsigned int e = 0;
167      while (round (x) >= 1000) {
168         x /= 10;
169         e++;
170      }
171      r = (unsigned long int) round (x);
172      while (e > 0) {
173         r *= 10;
174         e--;
175      }
176      printf ("%7lu", r);
177   }
178}
179
180
181/* compute the score for the operation arrayfunc[op] */
182static void
183compute_score (double *zscore, int op, gmp_randstate_t randstate)
184{
185  mpc_t *xptr, *yptr, *zptr;
186
187  int i, j;
188  size_t k;
189
190  unsigned long niter, ti;
191
192  double t;
193
194  double ops_per_time;
195
196  int countprec = 0;
197
198  *zscore = 1.0;
199
200  i = op;
201  for (k = 0; k < (int)sizeof (arrayprecision_op1) / sizeof (arrayprecision_op1[0]);
202       k++, countprec++)
203    {
204
205      mpfr_prec_t precision1 = arrayprecision_op1[k];
206      mpfr_prec_t precision2 = arrayprecision_op2[k];
207      mpfr_prec_t precision3 = arrayprecision_op2[k];
208      /* allocate array of random numbers */
209      xptr = bench_random_array (NB_RAND_CPLX, precision1, randstate);
210      yptr = bench_random_array (NB_RAND_CPLX, precision2, randstate);
211      zptr = bench_random_array (NB_RAND_CPLX, precision3, randstate);
212
213      /* compute the number of operations per seconds */
214      if (arrayfunc[i].noperands==2)
215          printf ("op %4s, prec %5lux%5lu->%5lu:",
216                  arrayfunc[i].name, precision1, precision2, precision3);
217      else
218          printf ("op %4s, prec %5lu      ->%5lu:",
219                  arrayfunc[i].name, precision1, precision3);
220      fflush (stdout);
221
222      t = arrayfunc[i].func_init (NB_RAND_CPLX, zptr, xptr, yptr);
223      niter = 1 + (unsigned long) (1e6 / t);
224
225      printf ("%9lu iter:", niter);
226      fflush (stdout);
227
228      /* ti expressed in microseconds */
229      niter = (niter + 9) / 10;
230      ti = arrayfunc[i].func_accurate (niter, NB_RAND_CPLX, zptr, xptr, yptr, arrayfunc[i].noperands);
231
232      ops_per_time = 1e5 * niter / (double) ti;
233         /* use 0.1s */
234
235      sensible_print (ops_per_time);
236      printf ("\n");
237
238      *zscore *= ops_per_time;
239
240      /* free memory */
241      for (j = 0; j < NB_RAND_CPLX; j++)
242        {
243          mpc_clear (xptr[j]);
244          mpc_clear (yptr[j]);
245          mpc_clear (zptr[j]);
246        }
247      free (xptr);
248      free (yptr);
249      free (zptr);
250    }
251
252  *zscore = pow (*zscore, 1.0 / (double) countprec);
253}
254
255/* compute the score for all groups */
256static void
257compute_groupscore (double groupscore[], int countop, double zscore[])
258{
259  int op;
260  enum egroupfunc group;
261  int countgroupop;
262
263  for (group = (enum egroupfunc)0; group != egroup_last; group++)
264    {
265      groupscore[group] = 1.0;
266      for (op = 0, countgroupop = 0; op < countop; op++)
267        {
268          if (group == arrayfunc[op].group)
269            {
270              groupscore[group] *= zscore[op];
271              countgroupop++;
272            }
273        }
274      groupscore[group] = pow (groupscore[group], 1.0 / (double) countgroupop);
275    }
276}
277
278
279/* compute the global score */
280static void
281compute_globalscore (double *globalscore, int countop, double zscore[])
282{
283  int op;
284
285  *globalscore = 1.0;
286  for (op = 0; op < countop; op++)
287    *globalscore *= zscore[op];
288  *globalscore = pow (*globalscore, 1.0 / (double) countop);
289}
290
291int
292main (void)
293{
294  int i;
295
296  double score[NB_BENCH_OP];
297
298  double globalscore, groupscore[egroup_last];
299
300  gmp_randstate_t randstate;
301
302  gmp_randinit_default (randstate);
303
304  for (i = 0; i < NB_BENCH_OP; i++)
305      compute_score (&(score[i]), i, randstate);
306  compute_globalscore (&globalscore, NB_BENCH_OP, score);
307  compute_groupscore (groupscore, NB_BENCH_OP, score);
308
309  printf ("\n=================================================================\n\n");
310  printf ("GMP: %s,  MPFR: %s,  MPC: %s\n", gmp_version,
311          mpfr_get_version (), mpc_get_version ());
312#ifdef __GMP_CC
313  printf ("GMP compiler: %s\n", __GMP_CC);
314#endif
315#ifdef __GMP_CFLAGS
316  printf ("GMP flags   : %s\n", __GMP_CFLAGS);
317#endif
318  printf ("\n");
319
320  for (i = 0; i < NB_BENCH_OP; i++)
321    {
322      printf ("   score for %4s  ", arrayfunc[i].name);
323      sensible_print (score[i]);
324      printf ("\n");
325      if (i == NB_BENCH_OP-1 || arrayfunc[i +1].group != arrayfunc[i].group)
326        {
327          enum egroupfunc g = arrayfunc[i].group;
328          printf ("group score %s", groupname[g]);
329          sensible_print (groupscore[g]);
330          printf ("\n\n");
331        }
332    }
333  printf ("global score       ");
334  sensible_print (globalscore);
335  printf ("\n\n");
336
337  gmp_randclear (randstate);
338  return 0;
339}
340