mpz_t caching

Vincent Lefevre vincent at vinc17.net
Fri Sep 4 08:00:29 UTC 2015


Hi,

In 2014, Patrick Pelissier (in Cc) implemented a mpz_t allocation
cache for MPFR, redefining mpz_init and mpz_clear, in order to
avoid some deallocations/allocations (via the indirect call to
the allocation functions) when mpz_t's cleared and init'ed again
a bit after. I've attached the patch that was applied to MPFR.

I don't think that such a feature could benefit only MPFR, so that
it may be a better idea to integrate something like that directly
in GMP. What do you think?

Note: A drawback is that it may be necessary to free the caches at
the end of the program so that tools like valgrind don't complain.
Not sure also about the security, in case some applications use
specific allocation functions to really clear the memory when a
mpz_t is cleared.

-- 
Vincent Lefèvre <vincent at vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
-------------- next part --------------
Index: src/exp_2.c
===================================================================
--- src/exp_2.c	(revision 8910)
+++ src/exp_2.c	(revision 8911)
@@ -194,7 +194,7 @@
           MPFR_ASSERTD (MPFR_IS_POS (r));
           mpfr_div_2ui (r, r, K, MPFR_RNDU); /* r = (x-n*log(2))/2^K, exact */
 
-          mpz_init2 (ss, q+GMP_NUMB_BITS);
+          mpz_init (ss);
           /* s <- 1 + r/1! + r^2/2! + ... + r^l/l! */
           MPFR_ASSERTD (MPFR_IS_PURE_FP (r) && MPFR_EXP (r) < 0);
           l = (precy < MPFR_EXP_2_THRESHOLD)
@@ -263,8 +263,8 @@
 
   expt = 0;
   *exps = 1 - (mpfr_exp_t) q;                   /* s = 2^(q-1) */
-  mpz_init2 (t, 2*q + GMP_NUMB_BITS);
-  mpz_init2 (rr, q + GMP_NUMB_BITS);
+  mpz_init (t);
+  mpz_init (rr);
   mpz_set_ui(t, 1);
   mpz_set_ui(s, 1);
   mpz_mul_2exp(s, s, q-1);
Index: src/mpfr-impl.h
===================================================================
--- src/mpfr-impl.h	(revision 8910)
+++ src/mpfr-impl.h	(revision 8911)
@@ -1989,8 +1989,23 @@
 
 __MPFR_DECLSPEC void mpfr_gamma_one_and_two_third _MPFR_PROTO((mpfr_ptr, mpfr_ptr, mpfr_prec_t));
 
+__MPFR_DECLSPEC void mpfr_mpz_init _MPFR_PROTO((mpz_ptr));
+__MPFR_DECLSPEC void mpfr_mpz_clear _MPFR_PROTO((mpz_ptr));
+
 #if defined (__cplusplus)
 }
 #endif
 
+/******************************************************
+ **************  Internal mpz caching *****************
+ ******************************************************/
+
+/* Cache for mpz_t */
+#if !defined(MPFR_MY_MPZ_INIT) || MPFR_MY_MPZ_INIT != 0
+# undef mpz_init
+# undef mpz_clear
+# define mpz_init mpfr_mpz_init
+# define mpz_clear mpfr_mpz_clear
 #endif
+
+#endif
Index: src/free_cache.c
===================================================================
--- src/free_cache.c	(revision 8910)
+++ src/free_cache.c	(revision 8911)
@@ -22,28 +22,66 @@
 
 #include "mpfr-impl.h"
 
-#if 0
-static void
-free_l2b (void)
+/* Default value for the cache of mpz_t */
+#ifndef MPFR_MY_MPZ_INIT
+# define MPFR_MY_MPZ_INIT 32
+#endif
+
+/* If the number of value to cache 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)
 {
-  int i, b;
+  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);
+    }
+}
 
-  for (b = 2; b <= BASE_MAX; b++)
-    for (i = 0; i < 2; i++)
-      {
-        mpfr_ptr p = __gmpfr_l2b[b-2][i];
-        if (p != NULL)
-          {
-            mpfr_clear (p);
-            (*__gmp_free_func) (p, sizeof (mpfr_t));
-          }
-      }
+MPFR_HOT_FUNCTION_ATTR void
+mpfr_mpz_clear (mpz_t z)
+{
+  if (MPFR_LIKELY (n_alloc < numberof (mpz_tab)))
+    {
+      /* 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
 
 void
 mpfr_free_cache (void)
 {
+#if MPFR_MY_MPZ_INIT
+  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
+
 #ifndef MPFR_USE_LOGGING
   mpfr_clear_cache (__gmpfr_cache_const_pi);
   mpfr_clear_cache (__gmpfr_cache_const_log2);
@@ -55,5 +93,4 @@
 #endif
   mpfr_clear_cache (__gmpfr_cache_const_euler);
   mpfr_clear_cache (__gmpfr_cache_const_catalan);
-  /* free_l2b (); */
 }


More information about the gmp-devel mailing list