From e4251c01b782221d11b33688c54e3d5b75d672dd Mon Sep 17 00:00:00 2001 From: gastineau Date: Wed, 22 Jan 2014 10:48:33 +0000 Subject: add the benchmark git-svn-id: svn://scm.gforge.inria.fr/svn/mpc/trunk@1426 211d60ee-9f03-0410-a15a-8952a2c7a4e4 --- Makefile.am | 4 +- configure.ac | 6 +- tools/Makefile.am | 21 ++++ tools/bench/Makefile.am | 29 +++++ tools/bench/benchtime.h | 66 ++++++++++ tools/bench/mpcbench.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 tools/Makefile.am create mode 100644 tools/bench/Makefile.am create mode 100644 tools/bench/benchtime.h create mode 100644 tools/bench/mpcbench.c diff --git a/Makefile.am b/Makefile.am index 940bd64..b72cca7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am -- Process this file with automake to produce Makefile.in ## -## Copyright (C) 2008, 2010, 2011 INRIA +## Copyright (C) 2008, 2010, 2011, 2012, 2013, 2014 INRIA ## ## This file is part of GNU MPC. ## @@ -22,7 +22,7 @@ ACLOCAL_AMFLAGS = -I m4 # version number for distribution tarball VERSION = @VERSION@@SVNVERSION@ -SUBDIRS = src tests doc +SUBDIRS = src tests doc tools EXTRA_HEADERS = src/mpc-log.h include_HEADERS = src/mpc.h @MPC_LOG_H@ diff --git a/configure.ac b/configure.ac index 39f8891..207d394 100644 --- a/configure.ac +++ b/configure.ac @@ -135,7 +135,7 @@ fi # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([locale.h inttypes.h stdint.h limits.h unistd.h sys/time.h]) +AC_CHECK_HEADERS([locale.h inttypes.h stdint.h limits.h unistd.h sys/time.h sys/resource.h]) AC_HEADER_TIME MPC_COMPLEX_H @@ -144,7 +144,7 @@ AC_C_CONST AC_TYPE_SIZE_T # Checks for libraries. -AC_CHECK_FUNCS([gettimeofday localeconv setlocale]) +AC_CHECK_FUNCS([gettimeofday localeconv setlocale getrusage]) AC_CHECK_FUNCS([dup dup2],, [AC_DEFINE([MPC_NO_STREAM_REDIRECTION],1,[Do not check mpc_out_str on stdout])]) @@ -237,5 +237,5 @@ AC_DEFINE_UNQUOTED([MPC_GCC_VERSION], ["$GCC_VERSION"], [Version of gcc]) # Looks for svn version if the version string contains "dev" MPC_SVNVERSION -AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile doc/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile doc/Makefile tools/Makefile tools/bench/Makefile]) AC_OUTPUT diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..b91ddf6 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,21 @@ +## tools/Makefile.am -- Process this file with automake to produce Makefile.in +## +## Copyright (C) 2014 INRIA +## +## This file is part of GNU MPC. +## +## GNU MPC 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. +## +## GNU MPC 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 this program. If not, see http://www.gnu.org/licenses/ . + +SUBDIRS = bench + diff --git a/tools/bench/Makefile.am b/tools/bench/Makefile.am new file mode 100644 index 0000000..7a554ec --- /dev/null +++ b/tools/bench/Makefile.am @@ -0,0 +1,29 @@ +## tools/bench/Makefile.am -- Process this file with automake to produce Makefile.in +## +## Copyright (C) 2014 INRIA +## +## This file is part of GNU MPC. +## +## GNU MPC 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. +## +## GNU MPC 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 this program. If not, see http://www.gnu.org/licenses/ . + +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_DEFAULT_SOURCE_EXT = .c + +LDADD = $(top_builddir)/src/libmpc.la + +noinst_PROGRAMS = mpcbench + + +noinst_HEADERS = benchtime.h + diff --git a/tools/bench/benchtime.h b/tools/bench/benchtime.h new file mode 100644 index 0000000..42a9dae --- /dev/null +++ b/tools/bench/benchtime.h @@ -0,0 +1,66 @@ +/* benchtime.h -- compute the timings for the benchmark. + +Copyright (C) 2014, INRIA + +This file is part of GNU MPC. + +GNU MPC 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. + +GNU MPC 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 this program. If not, see http://www.gnu.org/licenses/ . +*/ + + +/* compute the time to run accurately niter calls of the function for any number of inputs */ +#define DECLARE_ACCURATE_TIME_NOP(func, funccall) \ +unsigned long int ACCURATE_TIME_NOP##func( unsigned long int niter, int n, mpc_t* z, mpc_t* x, mpc_t* y, int nop);\ +unsigned long int ACCURATE_TIME_NOP##func( unsigned long int niter, int n, mpc_t* z, mpc_t* x, mpc_t* y, int nop)\ +{ \ + unsigned long int ti, i; int kn; \ + unsigned long int t0 = get_cputime (); \ + for (i = niter, kn=0; i > 0; i--) \ + { \ + funccall; \ + kn++; if (kn==n) kn = 0; \ + } \ + ti = get_cputime () - t0; \ + /* following lines are append to avoid warnings but minimize the number of macros*/\ + if (nop==2) y = NULL; \ + return ti; \ +} + +/* address of the function to time accurately niter calls of func */ +#define ADDR_ACCURATE_TIME_NOP(func) ACCURATE_TIME_NOP##func + +/* address of the function to time one call of func */ +#define ADDR_TIME_NOP(func) TIME_NOP##func + + +/* compute the time to run one only call of the function with two inputs */ +#define DECLARE_TIME_NOP(func, funcall, nop) \ + DECLARE_ACCURATE_TIME_NOP(func, funcall) \ + double TIME_NOP##func(int n, mpc_t* z, mpc_t* x, mpc_t* y); \ + double TIME_NOP##func(int n, mpc_t* z, mpc_t* x, mpc_t* y) \ + { \ + double t; unsigned long int nbcall, mytime; \ + for (nbcall = 1, mytime=0; mytime<250000; nbcall<<=1) \ + { \ + mytime = ACCURATE_TIME_NOP##func(nbcall, n, z, x, y, nop); \ + } \ + t = (double) mytime/ nbcall ; \ + return t; \ + } + +/* compute the time to run accurately niter calls of the function */ +/* functions with 2 operands */ +#define DECLARE_TIME_2OP(func) DECLARE_TIME_NOP(func, func(z[kn],x[kn],y[kn], MPC_RNDNN), 2 ) +/* functions with 1 operand */ +#define DECLARE_TIME_1OP(func) DECLARE_TIME_NOP(func, func(z[kn],x[kn], MPC_RNDNN), 1 ) diff --git a/tools/bench/mpcbench.c b/tools/bench/mpcbench.c new file mode 100644 index 0000000..f00a182 --- /dev/null +++ b/tools/bench/mpcbench.c @@ -0,0 +1,321 @@ +/* mpcbench.c -- perform the benchmark on the complex numbers. + +Copyright (C) 2014, INRIA + +This file is part of GNU MPC. + +GNU MPC 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. + +GNU MPC 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 this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "config.h" +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif +#include "mpc.h" +#include "benchtime.h" + +static unsigned long get_cputime (void); + +/* enumeration of the group of functions */ +enum egroupfunc +{ + egroup_arith = 0, /* e.g., arith ... */ + egroup_special, /* e.g., cos, ... */ + egroup_last /* to get the number of enum */ +}; + +/* name of the group of functions */ +const char *groupname [] = { +"Arith ", +"Special" +}; + + + +struct benchfunc +{ + const char *name; /* name of the function */ + double (*func_init) (int n, mpc_t * z, mpc_t * x, mpc_t * y); /* compute the time for one call (not accurate) */ + 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) */ + enum egroupfunc group; /* group of the function */ + int noperands; /* number of operands */ +}; + + +/* declare the function to compute the cost for one call of the mpc function */ +DECLARE_TIME_2OP (mpc_mul) +DECLARE_TIME_2OP (mpc_add) +DECLARE_TIME_2OP (mpc_sub) +DECLARE_TIME_2OP (mpc_div) +DECLARE_TIME_1OP (mpc_sqrt) +DECLARE_TIME_1OP (mpc_exp) +DECLARE_TIME_1OP (mpc_log) +DECLARE_TIME_1OP (mpc_sin) +DECLARE_TIME_1OP (mpc_cos) +DECLARE_TIME_1OP (mpc_asin) +DECLARE_TIME_1OP (mpc_acos) + +/* number of operations to score*/ +#define NB_BENCH_OP 11 +/* number of random numbers */ +#define NB_RAND_CPLX 10000 + +/* list of functions to compute the score */ +const struct benchfunc + arrayfunc[NB_BENCH_OP] = { + {"mul", ADDR_TIME_NOP (mpc_mul), ADDR_ACCURATE_TIME_NOP (mpc_mul), egroup_arith, 2}, + {"add", ADDR_TIME_NOP (mpc_add), ADDR_ACCURATE_TIME_NOP (mpc_add), egroup_arith, 2}, + {"sub", ADDR_TIME_NOP (mpc_sub), ADDR_ACCURATE_TIME_NOP (mpc_sub), egroup_arith, 2}, + {"div", ADDR_TIME_NOP (mpc_div), ADDR_ACCURATE_TIME_NOP (mpc_div), egroup_arith, 2}, + {"sqrt", ADDR_TIME_NOP (mpc_sqrt), ADDR_ACCURATE_TIME_NOP (mpc_sqrt), egroup_special, 1}, + {"exp", ADDR_TIME_NOP (mpc_exp), ADDR_ACCURATE_TIME_NOP (mpc_exp), egroup_special, 1}, + {"log", ADDR_TIME_NOP (mpc_log), ADDR_ACCURATE_TIME_NOP (mpc_log), egroup_special, 1}, + {"cos", ADDR_TIME_NOP (mpc_cos), ADDR_ACCURATE_TIME_NOP (mpc_cos), egroup_special, 1}, + {"sin", ADDR_TIME_NOP (mpc_sin), ADDR_ACCURATE_TIME_NOP (mpc_sin), egroup_special, 1}, + {"acos", ADDR_TIME_NOP (mpc_acos), ADDR_ACCURATE_TIME_NOP (mpc_acos), egroup_special, 1}, + {"asin", ADDR_TIME_NOP (mpc_asin), ADDR_ACCURATE_TIME_NOP (mpc_asin), egroup_special, 1} + }; + +/* the following arrays must have the same number of elemnts */ + +/* list of precision to test for the first operand */ +const int arrayprecision_op1[] = + { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 1500, 3000, 5000, +}; + +/* list of precision to test for the second operand */ +const int arrayprecision_op2[] = + { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 50, 100, 200, 350, 700, 1500, 3000, 6000, 10000, 3000, 6000, 10000 + +}; + +/*! get the time in microseconds */ +static unsigned long +get_cputime (void) +{ +#ifdef HAVE_GETRUSAGE + struct rusage ru; + + getrusage (RUSAGE_SELF, &ru); + return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec + +ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec; +#else + printf("\nthe function getrusage not available\n"); + exit(1); + return 0; +#endif +} + +/* initialize an array of n random complex numbers */ +static mpc_t * +bench_random_array (int n, mpfr_prec_t precision, gmp_randstate_t randstate) +{ + int j; + + mpc_t *ptr; + + ptr = (mpc_t *) malloc (n * sizeof (mpc_t)); + if (ptr == NULL) + { + printf ("Can't allocate memory for %d complex numbers\n", n); + exit (1); + return NULL; + } + for (j = 0; j < n; j++) + { + mpc_init2 (ptr[j], precision); + mpc_urandom (ptr[j], randstate); + /*mpc_out_str(stdout, 10, 20, ptr[j], MPC_RNDNN); + fputs("\n", stdout); */ + } + return ptr; +} + +/* compute the score for the operation arrayfunc[op] */ +static void +compute_score (mpz_t zscore, int op, gmp_randstate_t randstate) +{ + mpc_t *xptr, *yptr, *zptr; + + int i, j; + size_t k; + + unsigned long niter, ti; + + double t; + + unsigned long ops_per_sec; + + int countprec = 0; + + mpz_init_set_si (zscore, 1); + + i = op; + for (k = 0; k < (int)sizeof (arrayprecision_op1) / sizeof (arrayprecision_op1[0]); + k++, countprec++) + { + + mpfr_prec_t precision1 = arrayprecision_op1[k]; + mpfr_prec_t precision2 = arrayprecision_op2[k]; + mpfr_prec_t precision3 = arrayprecision_op2[k]; + /* allocate array of random numbers */ + xptr = bench_random_array (NB_RAND_CPLX, precision1, randstate); + yptr = bench_random_array (NB_RAND_CPLX, precision2, randstate); + zptr = bench_random_array (NB_RAND_CPLX, precision3, randstate); + + /* compute the number of operations per seconds */ + if (arrayfunc[i].noperands==2) + { + printf ("operation %5s, precision : %5lux%5lu to %5lu bits ... ", arrayfunc[i].name, precision1, precision2, precision3); + } + else + { + printf ("operation %5s, precision : %5lu to %5lu bits ... ", arrayfunc[i].name, precision1, precision3); + } + fflush (stdout); + + t = arrayfunc[i].func_init (NB_RAND_CPLX, zptr, xptr, yptr); + niter = 1 + (unsigned long) (1e6 / t); + + printf (" %10lu iterations ...", niter); + fflush (stdout); + + /* ti expressed in microseconds */ + ti = arrayfunc[i].func_accurate (niter, NB_RAND_CPLX, zptr, xptr, yptr, arrayfunc[i].noperands); + + ops_per_sec = (unsigned long) (1000000E0 * niter / (double) ti); + + printf (" %10lu operations per second\n", ops_per_sec); + + mpz_mul_ui (zscore, zscore, ops_per_sec); + + /* free memory */ + for (j = 0; j < NB_RAND_CPLX; j++) + { + mpc_clear (xptr[j]); + mpc_clear (yptr[j]); + mpc_clear (zptr[j]); + } + free (xptr); + free (yptr); + free (zptr); + } + + mpz_root (zscore, zscore, countprec); +} + +/* compute the score for all groups */ +static void +compute_groupscore (mpz_t groupscore[], int countop, mpz_t zscore[]) +{ + int op; + enum egroupfunc group; + int countgroupop; + + for (group = (enum egroupfunc)0; group != egroup_last; group++) + { + mpz_init_set_si (groupscore[group], 1); + for (op = 0, countgroupop = 0; op < countop; op++) + { + if (group == arrayfunc[op].group) + { + mpz_mul (groupscore[group], groupscore[group], zscore[op]); + countgroupop++; + } + } + mpz_root (groupscore[group], groupscore[group], countgroupop); + } +} + + +/* compute the global score */ +static void +compute_globalscore (mpz_t globalscore, int countop, mpz_t zscore[]) +{ + int op; + + mpz_init_set_si (globalscore, 1); + for (op = 0; op < countop; op++) + { + mpz_mul (globalscore, globalscore, zscore[op]); + } + mpz_root (globalscore, globalscore, countop); +} + +int +main (void) +{ + int i; + + enum egroupfunc group; + + mpz_t score[NB_BENCH_OP]; + + mpz_t globalscore, groupscore[egroup_last]; + + gmp_randstate_t randstate; + + + gmp_randinit_default (randstate); + + for (i = 0; i < NB_BENCH_OP; i++) + { + compute_score (score[i], i, randstate); + } + compute_globalscore (globalscore, NB_BENCH_OP, score); + compute_groupscore (groupscore, NB_BENCH_OP, score); + + printf ("\n=================================================================\n\n"); + printf ("GMP : %s MPFR : %s MPC: %s\n", gmp_version, + mpfr_get_version (), mpc_get_version ()); +#ifdef __GMP_CC + printf ("GMP compiler : %s\n", __GMP_CC); +#endif +#ifdef __GMP_CFLAGS + printf ("GMP flags : %s\n", __GMP_CFLAGS); +#endif + printf ("\n\n"); + + for (i = 0; i < NB_BENCH_OP; i++) + { + gmp_printf ("\tscore for %5s : %12Zd\n", arrayfunc[i].name, score[i]); + if (i == NB_BENCH_OP-1 || arrayfunc[i +1].group != arrayfunc[i].group) + { + enum egroupfunc g = arrayfunc[i].group; + gmp_printf ("group score %s : %12Zd\n\n", groupname[g], groupscore[g]); + } + } + gmp_printf ("global score : %12Zd\n\n", globalscore); + + + for (i = 0; i < NB_BENCH_OP; i++) + { + mpz_clear (score[i]); + } + + for (group = (enum egroupfunc)0; group != egroup_last; group++) + { + mpz_clear (groupscore[group]); + } + mpz_clear (globalscore); + gmp_randclear (randstate); + return 0; +} -- cgit v1.2.1