diff options
author | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-12-06 16:34:16 +0000 |
---|---|---|
committer | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-12-06 16:34:16 +0000 |
commit | f2dfd7d60c02b7ca46a6182ab21416fa52f9bf6c (patch) | |
tree | c82e77c4d6e937feadebe11f28137e570e74412d | |
parent | 6872fdc006aaf96157958ade355eaa0c9d58080d (diff) | |
download | mpfr-f2dfd7d60c02b7ca46a6182ab21416fa52f9bf6c.tar.gz |
Cleanup and fixes for the mpz_t pool.
* Split src/free_cache.c to itself and a new source file src/pool.c
containing pool related code.
* src/mpfr-impl.h, src/pool.c: cleanup and fixes. In particular:
- renamed MPFR_MY_MPZ_INIT to MPFR_POOL_NENTRIES;
- no longer use GMP's symbols __gmpz_init, etc. directly;
- improved the condition for mpfr_mpz_init2;
- fixed the condition in mpfr_mpz_clear (we want to consider
the allocated size, not the size of the current number).
* src/Makefile.am: added pool.c.
* src/mpfr.h: added mpfr_free_pool prototype.
* doc/mpfr.texi: updated pool description in "Memory Handling";
new function mpfr_free_pool.
* doc/README.dev: update due to the rename of MPFR_MY_MPZ_INIT.
* NEWS: better announcement of the mpz_t pool.
git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/trunk@11922 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | doc/README.dev | 2 | ||||
-rw-r--r-- | doc/mpfr.texi | 18 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/free_cache.c | 92 | ||||
-rw-r--r-- | src/mpfr-impl.h | 23 | ||||
-rw-r--r-- | src/mpfr.h | 1 | ||||
-rw-r--r-- | src/pool.c | 117 |
8 files changed, 153 insertions, 107 deletions
@@ -87,7 +87,10 @@ Changes from versions 3.1.* to version 4.0.0: - Old, deprecated macros mpfr_add_one_ulp and mpfr_sub_one_ulp removed. The mpfr_next* functions should be used instead. - Internally, improved caching: a minimum of 10% increase of the precision - is guaranteed to avoid too many recomputations; added mpz_t caching. + is guaranteed to avoid too many recomputations. +- Added internal small-precision mpz_t pool, which aims to avoid the + overhead of memory allocation, in particular. + New function mpfr_free_pool. - Added configure option --enable-assert=none to avoid checking any assertion. - The --enable-decimal-float configure option no longer requires --with-gmp-build, and support for decimal floats is now automatically diff --git a/doc/README.dev b/doc/README.dev index ca2eb266e..170307aaa 100644 --- a/doc/README.dev +++ b/doc/README.dev @@ -237,7 +237,7 @@ To make a release (for the MPFR team): Check with "-UHAVE_BIG_ENDIAN -UHAVE_LITTLE_ENDIAN" to simulate platforms where the endianness is unknown (or can't be specified without AC_CONFIG_HEADERS). - Check also without mpz_t caching (-DMPFR_MY_MPZ_INIT=0). + Check also without the mpz_t pool (-DMPFR_POOL_NENTRIES=0). Check the generic code, e.g. with -DMPFR_GENERIC_ABI in $CFLAGS (useful because most tests are written for low precision) and with mpfr_cv_c_long_double_format=unknown (as a variable assignment). diff --git a/doc/mpfr.texi b/doc/mpfr.texi index 2eef02008..93d3c1c01 100644 --- a/doc/mpfr.texi +++ b/doc/mpfr.texi @@ -981,12 +981,10 @@ When more precision is needed, the value is automatically recomputed; a minimum of 10% increase of the precision is guaranteed to avoid too many recomputations. -MPFR functions may also create pools for internal use to avoid the cost -of memory allocation. -@c FIXME: Say a bit more about the mpz_t pool. Make it configurable at -@c run time, with a compile-time bound on the number of entries? Pools -@c may be a bad idea if the allocator is fast and/or if there are many -@c threads (since they are thread-local). +MPFR functions may also create thread-local pools for internal use +to avoid the cost of memory allocation. The pools can be freed with +@code{mpfr_free_pool} (but with a default MPFR build, they should not +take much memory, as the allocation size is limited). At any time, the user can free various caches and pools with @code{mpfr_free_cache} and @code{mpfr_free_cache2}. It is strongly advised @@ -2398,6 +2396,12 @@ Note: @code{mpfr_free_cache2(MPFR_FREE_LOCAL_CACHE|MPFR_FREE_GLOBAL_CACHE)} is currently equivalent to @code{mpfr_free_cache()}. @end deftypefun +@deftypefun void mpfr_free_pool (void) +Free the pools used by MPFR internally. +Note: This function is automatically called after the thread-local caches +are freed (with @code{mpfr_free_cache} or @code{mpfr_free_cache2}). +@end deftypefun + @deftypefun int mpfr_mp_memory_cleanup (void) This function should be called before calling @code{mp_set_memory_functions}. @xref{Memory Handling}, for more information. @@ -3967,6 +3971,8 @@ in MPFR 4.0. @item @code{mpfr_free_cache2} in MPFR 4.0. +@item @code{mpfr_free_pool} in MPFR 4.0. + @item @code{mpfr_frexp} in MPFR 3.1. @item @code{mpfr_gamma_inc} in MPFR 4.0. diff --git a/src/Makefile.am b/src/Makefile.am index 9251ee377..810f27397 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,7 +60,7 @@ scale2.c set_z_exp.c ai.c gammaonethird.c ieee_floats.h \ grandom.c fpif.c set_float128.c get_float128.c rndna.c nrandom.c \ random_deviate.h random_deviate.c erandom.c mpfr-mini-gmp.c \ mpfr-mini-gmp.h fmma.c log_ui.c gamma_inc.c ubf.c invert_limb.h \ -invsqrt_limb.h beta.c odd_p.c get_q.c +invsqrt_limb.h beta.c odd_p.c get_q.c pool.c libmpfr_la_LIBADD = @LIBOBJS@ diff --git a/src/free_cache.c b/src/free_cache.c index ca8327d9c..dcc5b0478 100644 --- a/src/free_cache.c +++ b/src/free_cache.c @@ -22,80 +22,7 @@ http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., #include "mpfr-impl.h" -#ifndef MPFR_MY_MPZ_INIT -# define MPFR_MY_MPZ_INIT 32 /* number of entries for the mpz_t pool */ -# define MPFR_POOL_MAX_SIZE 32 /* maximal size (in limbs) of each entry */ -#endif - -/* If the number of entries of the mpz_t pool is not zero */ -#if MPFR_MY_MPZ_INIT - -/* Index in the stack table of mpz_t and stack table of mpz_t */ -static MPFR_THREAD_ATTR int n_alloc = 0; -static MPFR_THREAD_ATTR __mpz_struct mpz_tab[MPFR_MY_MPZ_INIT]; - -MPFR_HOT_FUNCTION_ATTR void -mpfr_mpz_init (mpz_t z) -{ - if (MPFR_LIKELY (n_alloc > 0)) - { - /* Get a mpz_t from the MPFR stack of previously used mpz_t. - It reduces memory pressure, and it allows to reuse - a mpz_t which should be sufficiently big. */ - MPFR_ASSERTD (n_alloc <= numberof (mpz_tab)); - memcpy (z, &mpz_tab[--n_alloc], sizeof (mpz_t)); - SIZ(z) = 0; - } - else - { - /* Call real GMP function */ - (__gmpz_init) (z); - } -} - -MPFR_HOT_FUNCTION_ATTR void -mpfr_mpz_init2 (mpz_t z, mp_bitcnt_t n) -{ - if (MPFR_LIKELY (n_alloc > 0)) - { - /* Get a mpz_t from the MPFR stack of previously used mpz_t. - It reduces memory pressure, and it allows to reuse - a mpz_t which should be sufficiently big. */ - MPFR_ASSERTD (n_alloc <= numberof (mpz_tab)); - memcpy (z, &mpz_tab[--n_alloc], sizeof (mpz_t)); - SIZ(z) = 0; - } - else - { - /* Call real GMP function */ - (__gmpz_init2) (z, n); - } -} - - -MPFR_HOT_FUNCTION_ATTR void -mpfr_mpz_clear (mpz_t z) -{ - /* we only put objects with at most MPFR_POOL_MAX_SIZE in the mpz_t pool, - to avoid it takes too much memory (and anyway the speedup is mainly - for small precision) */ - if (MPFR_LIKELY (n_alloc < numberof (mpz_tab) && - mpz_size (z) <= MPFR_POOL_MAX_SIZE)) - { - /* Push back the mpz_t inside the stack of the used mpz_t */ - MPFR_ASSERTD (n_alloc >= 0); - memcpy (&mpz_tab[n_alloc++], z, sizeof (mpz_t)); - } - else - { - /* Call real GMP function */ - (__gmpz_clear) (z); - } -} - -#endif - -/* Theses caches may be global to all threads or local to the current */ +/* Theses caches may be global to all threads or local to the current one. */ static void mpfr_free_const_caches (void) { @@ -112,22 +39,15 @@ mpfr_free_const_caches (void) mpfr_clear_cache (__gmpfr_cache_const_catalan); } -/* Theses caches are always local to a thread */ +/* Theses caches/pools are always local to a thread. */ static void mpfr_free_local_cache (void) { - /* Before mpz caching */ + /* Before freeing the mpz_t pool, we need to free any cache of + mpz_t numbers, since freeing such a cache may add entries to + the mpz_t pool. */ mpfr_bernoulli_freecache (); - -#if MPFR_MY_MPZ_INIT - { /* Avoid mixed declarations and code (for ISO C90 support). */ - int i; - MPFR_ASSERTD (n_alloc >= 0 && n_alloc <= numberof (mpz_tab)); - for (i = 0; i < n_alloc; i++) - (__gmpz_clear) (&mpz_tab[i]); - n_alloc = 0; - } -#endif + mpfr_free_pool (); } void diff --git a/src/mpfr-impl.h b/src/mpfr-impl.h index 0c55b8f72..d035c349c 100644 --- a/src/mpfr-impl.h +++ b/src/mpfr-impl.h @@ -2272,30 +2272,29 @@ __MPFR_DECLSPEC int mpfr_vasnprintf_aux (char**, char*, size_t, const char*, #endif -/****************************************************** - *************** Internal mpz caching *************** - ******************************************************/ +/***************************************************** + *************** Internal mpz_t pool *************** + *****************************************************/ -/* don't use mpz caching with mini-gmp */ +/* don't use the mpz_t pool with mini-gmp */ #ifdef MPFR_USE_MINI_GMP -#define MPFR_MY_MPZ_INIT 0 +# define MPFR_POOL_NENTRIES 0 #endif -/* define MPFR_MY_MPZ_INIT to 0 here to disable the mpz_t pool - (see src/free_cache.c) */ -/* #define MPFR_MY_MPZ_INIT 0 */ +#ifndef MPFR_POOL_NENTRIES +# define MPFR_POOL_NENTRIES 32 /* default number of entries of the pool */ +#endif -/* Cache for mpz_t */ -#if !defined(MPFR_MY_MPZ_INIT) || MPFR_MY_MPZ_INIT != 0 +#if MPFR_POOL_NENTRIES && !defined(MPFR_POOL_DONT_REDEFINE) # undef mpz_init # undef mpz_init2 # undef mpz_clear +# undef mpz_init_set_ui +# undef mpz_init_set # define mpz_init mpfr_mpz_init # define mpz_init2 mpfr_mpz_init2 # define mpz_clear mpfr_mpz_clear -# undef mpz_init_set_ui # define mpz_init_set_ui(a,b) do { mpz_init (a); mpz_set_ui (a, b); } while (0) -# undef mpz_init_set # define mpz_init_set(a,b) do { mpz_init (a); mpz_set (a, b); } while (0) #endif diff --git a/src/mpfr.h b/src/mpfr.h index cf472766b..e731e00d0 100644 --- a/src/mpfr.h +++ b/src/mpfr.h @@ -737,6 +737,7 @@ __MPFR_DECLSPEC int mpfr_sum (mpfr_ptr, const mpfr_ptr *, unsigned long, __MPFR_DECLSPEC void mpfr_free_cache (void); __MPFR_DECLSPEC void mpfr_free_cache2 (mpfr_free_cache_t); +__MPFR_DECLSPEC void mpfr_free_pool (void); __MPFR_DECLSPEC int mpfr_mp_memory_cleanup (void); __MPFR_DECLSPEC int mpfr_subnormalize (mpfr_ptr, int, mpfr_rnd_t); diff --git a/src/pool.c b/src/pool.c new file mode 100644 index 000000000..d031543ab --- /dev/null +++ b/src/pool.c @@ -0,0 +1,117 @@ +/* mpfr_free_cache... - Free cache/pool memory used by MPFR. + +Copyright 2004-2017 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 +http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#define MPFR_POOL_DONT_REDEFINE +#include "mpfr-impl.h" + +#ifndef MPFR_POOL_MAX_SIZE +# define MPFR_POOL_MAX_SIZE 32 /* maximal size (in limbs) for each entry */ +#endif + +/* If the number of entries of the mpz_t pool is not zero */ +#if MPFR_POOL_NENTRIES + +/* Index in the stack table of mpz_t and stack table of mpz_t */ +static MPFR_THREAD_ATTR int n_alloc = 0; +static MPFR_THREAD_ATTR __mpz_struct mpz_tab[MPFR_POOL_NENTRIES]; + +MPFR_HOT_FUNCTION_ATTR void +mpfr_mpz_init (mpz_t z) +{ + if (MPFR_LIKELY (n_alloc > 0)) + { + /* Get a mpz_t from the MPFR stack of previously used mpz_t. + It reduces memory pressure, and it allows to reuse + a mpz_t that should be sufficiently big. */ + MPFR_ASSERTD (n_alloc <= numberof (mpz_tab)); + memcpy (z, &mpz_tab[--n_alloc], sizeof (mpz_t)); + SIZ(z) = 0; + } + else + { + /* Call the real GMP function */ + mpz_init (z); + } +} + +MPFR_HOT_FUNCTION_ATTR void +mpfr_mpz_init2 (mpz_t z, mp_bitcnt_t n) +{ + /* The condition on n is used below as the argument n will be ignored if + the mpz_t is obtained from the MPFR stack of previously used mpz_t. + Said otherwise, it z is expected to have a large size at the end, then + it is better to allocate this size directly than to get a mpz_t of + small size, with possibly several realloc's on it. But if n satisfies + the condition and is larger than the stacked mpz_t, this may still + yield useless realloc's. This is not ideal. We might consider to use + mpz_init2 with the maximum size in mpfr_mpz_init to solve this issue. */ + if (MPFR_LIKELY (n_alloc > 0 && n <= MPFR_POOL_MAX_SIZE * GMP_NUMB_BITS)) + { + /* Get a mpz_t from the MPFR stack of previously used mpz_t. + It reduces memory pressure, and it allows to reuse + a mpz_t that should be sufficiently big. */ + MPFR_ASSERTD (n_alloc <= numberof (mpz_tab)); + memcpy (z, &mpz_tab[--n_alloc], sizeof (mpz_t)); + SIZ(z) = 0; + } + else + { + /* Call the real GMP function */ + mpz_init2 (z, n); + } +} + + +MPFR_HOT_FUNCTION_ATTR void +mpfr_mpz_clear (mpz_t z) +{ + /* We only put objects with at most MPFR_POOL_MAX_SIZE in the mpz_t pool, + to avoid it takes too much memory (and anyway the speedup is mainly + for small precision). */ + if (MPFR_LIKELY (n_alloc < numberof (mpz_tab) && + ALLOC (z) <= MPFR_POOL_MAX_SIZE)) + { + /* Push back the mpz_t inside the stack of the used mpz_t */ + MPFR_ASSERTD (n_alloc >= 0); + memcpy (&mpz_tab[n_alloc++], z, sizeof (mpz_t)); + } + else + { + /* Call the real GMP function */ + mpz_clear (z); + } +} + +#endif + +void +mpfr_free_pool (void) +{ +#if MPFR_POOL_NENTRIES + int i; + + MPFR_ASSERTD (n_alloc >= 0 && n_alloc <= numberof (mpz_tab)); + for (i = 0; i < n_alloc; i++) + mpz_clear (&mpz_tab[i]); + n_alloc = 0; +#endif +} |