This is needed for mpfr 4.2.1. See: https://github.com/aleaxit/gmpy/issues/418#issuecomment-1706698224 From 7351e2eb1abf4b37a47a822eb3f3f29f90c7f854 Mon Sep 17 00:00:00 2001 From: Case Van Horsen Date: Wed, 28 Dec 2022 21:11:39 -0800 Subject: [PATCH] Cleanup gmpy2 object caching. --- src/gmpy2.c | 39 +++++----- src/gmpy2_cache.c | 190 +++++++++++---------------------------------- src/gmpy2_cache.h | 6 -- src/gmpy2_misc.c | 47 ----------- src/gmpy2_misc.h | 2 - test/runtests.py | 2 - test/test_misc.txt | 35 --------- 7 files changed, 65 insertions(+), 256 deletions(-) diff --git a/src/gmpy2.c b/src/gmpy2.c index b52b82fb..6ed9139a 100644 --- a/src/gmpy2.c +++ b/src/gmpy2.c @@ -541,29 +541,37 @@ LGPL 3 or later."; /* The following global structures are used by gmpy_cache.c. */ -struct gmpy_global { - int cache_size; /* size of cache, for all caches */ - int cache_obsize; /* maximum size of the objects that are cached */ +#define CACHE_SIZE (100) +#define MAX_CACHE_MPZ_LIMBS (64) +#define MAX_CACHE_MPFR_BITS (1024) + +typedef struct { mpz_t tempz; /* Temporary variable used for integer conversions */ - MPZ_Object **gmpympzcache; + MPZ_Object *gmpympzcache[CACHE_SIZE]; int in_gmpympzcache; - XMPZ_Object **gmpyxmpzcache; + XMPZ_Object *gmpyxmpzcache[CACHE_SIZE]; int in_gmpyxmpzcache; - MPQ_Object **gmpympqcache; + MPQ_Object *gmpympqcache[CACHE_SIZE]; int in_gmpympqcache; - MPFR_Object **gmpympfrcache; + MPFR_Object *gmpympfrcache[CACHE_SIZE]; int in_gmpympfrcache; - MPC_Object **gmpympccache; + MPC_Object *gmpympccache[CACHE_SIZE]; int in_gmpympccache; +} gmpy_global; + +static gmpy_global global = { + .in_gmpympzcache = 0, + .in_gmpyxmpzcache = 0, + .in_gmpympqcache = 0, + .in_gmpympfrcache = 0, + .in_gmpympccache = 0, }; -static struct gmpy_global global; - /* Support for context manager. */ #ifdef WITHOUT_THREADS @@ -716,7 +724,6 @@ static PyMethodDef Pygmpy_methods [] = { "f_mod_2exp", GMPy_MPZ_f_mod_2exp, METH_VARARGS, doc_f_mod_2exp }, { "gcd", GMPy_MPZ_Function_GCD, METH_VARARGS, GMPy_doc_mpz_function_gcd }, { "gcdext", GMPy_MPZ_Function_GCDext, METH_VARARGS, GMPy_doc_mpz_function_gcdext }, - { "get_cache", GMPy_get_cache, METH_NOARGS, GMPy_doc_get_cache }, { "hamdist", GMPy_MPZ_hamdist, METH_VARARGS, doc_hamdist }, { "invert", GMPy_MPZ_Function_Invert, METH_VARARGS, GMPy_doc_mpz_function_invert }, { "iroot", GMPy_MPZ_Function_Iroot, METH_VARARGS, GMPy_doc_mpz_function_iroot }, @@ -777,7 +784,6 @@ static PyMethodDef Pygmpy_methods [] = { "qdiv", GMPy_MPQ_Function_Qdiv, METH_VARARGS, GMPy_doc_function_qdiv }, { "remove", GMPy_MPZ_Function_Remove, METH_VARARGS, GMPy_doc_mpz_function_remove }, { "random_state", GMPy_RandomState_Factory, METH_VARARGS, GMPy_doc_random_state_factory }, - { "set_cache", GMPy_set_cache, METH_VARARGS, GMPy_doc_set_cache }, { "sign", GMPy_Context_Sign, METH_O, GMPy_doc_function_sign }, { "square", GMPy_Context_Square, METH_O, GMPy_doc_function_square }, { "sub", GMPy_Context_Sub, METH_VARARGS, GMPy_doc_sub }, @@ -1075,17 +1081,8 @@ PyMODINIT_FUNC PyInit_gmpy2(void) } /* Initialize the global structure. Eventually this should be module local. */ - global.cache_size = 100; - global.cache_obsize = 128; mpz_init(global.tempz); - /* Initialize object caching. */ - set_gmpympzcache(); - set_gmpympqcache(); - set_gmpyxmpzcache(); - set_gmpympfrcache(); - set_gmpympccache(); - /* Initialize exceptions. */ GMPyExc_GmpyError = PyErr_NewException("gmpy2.gmpy2Error", PyExc_ArithmeticError, NULL); if (!GMPyExc_GmpyError) { diff --git a/src/gmpy2_cache.c b/src/gmpy2_cache.c index c00fc8a0..f2af6831 100644 --- a/src/gmpy2_cache.c +++ b/src/gmpy2_cache.c @@ -27,29 +27,10 @@ /* gmpy2 caches objects so they can be reused quickly without involving a new * memory allocation or object construction. - * - * The "py???cache" is used to cache Py??? objects. The cache is accessed - * via Py???_new/Py???_dealloc. The functions set_py???cache and - * set_py???cache are used to change the size of the array used to the store - * the cached objects. */ /* Caching logic for Pympz. */ -static void -set_gmpympzcache(void) -{ - if (global.in_gmpympzcache > global.cache_size) { - int i; - for (i = global.cache_size; i < global.in_gmpympzcache; ++i) { - mpz_clear(global.gmpympzcache[i]->z); - PyObject_Del(global.gmpympzcache[i]); - } - global.in_gmpympzcache = global.cache_size; - } - global.gmpympzcache = realloc(global.gmpympzcache, sizeof(MPZ_Object)*global.cache_size); -} - /* GMPy_MPZ_New returns a reference to a new MPZ_Object. Its value * is initialized to 0. */ @@ -61,18 +42,17 @@ GMPy_MPZ_New(CTXT_Object *context) if (global.in_gmpympzcache) { result = global.gmpympzcache[--(global.in_gmpympzcache)]; - /* Py_INCREF does not set the debugging pointers, so need to use - * _Py_NewReference instead. */ - _Py_NewReference((PyObject*)result); + Py_INCREF((PyObject*)result); mpz_set_ui(result->z, 0); - result->hash_cache = -1; } else { - if ((result = PyObject_New(MPZ_Object, &MPZ_Type))) { - mpz_init(result->z); - result->hash_cache = -1; + result = PyObject_New(MPZ_Object, &MPZ_Type); + if (result == NULL) { + return NULL; } + mpz_init(result->z); } + result->hash_cache = -1; return result; } @@ -199,8 +179,9 @@ GMPy_MPZ_NewInit(PyTypeObject *type, PyObject *args, PyObject *keywds) static void GMPy_MPZ_Dealloc(MPZ_Object *self) { - if (global.in_gmpympzcache < global.cache_size && - self->z->_mp_alloc <= global.cache_obsize) { + if (global.in_gmpympzcache < CACHE_SIZE && + self->z->_mp_alloc <= MAX_CACHE_MPZ_LIMBS) { + global.gmpympzcache[(global.in_gmpympzcache)++] = self; } else { @@ -211,20 +192,6 @@ GMPy_MPZ_Dealloc(MPZ_Object *self) /* Caching logic for Pyxmpz. */ -static void -set_gmpyxmpzcache(void) -{ - if (global.in_gmpyxmpzcache > global.cache_size) { - int i; - for (i = global.cache_size; i < global.in_gmpyxmpzcache; ++i) { - mpz_clear(global.gmpyxmpzcache[i]->z); - PyObject_Del(global.gmpyxmpzcache[i]); - } - global.in_gmpyxmpzcache = global.cache_size; - } - global.gmpyxmpzcache = realloc(global.gmpyxmpzcache, sizeof(XMPZ_Object)*global.cache_size); -} - static XMPZ_Object * GMPy_XMPZ_New(CTXT_Object *context) { @@ -232,15 +199,15 @@ GMPy_XMPZ_New(CTXT_Object *context) if (global.in_gmpyxmpzcache) { result = global.gmpyxmpzcache[--(global.in_gmpyxmpzcache)]; - /* Py_INCREF does not set the debugging pointers, so need to use - * _Py_NewReference instead. */ - _Py_NewReference((PyObject*)result); + Py_INCREF((PyObject*)result); mpz_set_ui(result->z, 0); } else { - if ((result = PyObject_New(XMPZ_Object, &XMPZ_Type))) { - mpz_init(result->z); + result = PyObject_New(XMPZ_Object, &XMPZ_Type); + if (result == NULL) { + return NULL; } + mpz_init(result->z); } return result; } @@ -346,34 +313,21 @@ GMPy_XMPZ_NewInit(PyTypeObject *type, PyObject *args, PyObject *keywds) } static void -GMPy_XMPZ_Dealloc(XMPZ_Object *obj) +GMPy_XMPZ_Dealloc(XMPZ_Object *self) { - if (global.in_gmpyxmpzcache < global.cache_size && - obj->z->_mp_alloc <= global.cache_obsize) { - global.gmpyxmpzcache[(global.in_gmpyxmpzcache)++] = obj; + if (global.in_gmpyxmpzcache < CACHE_SIZE && + self->z->_mp_alloc <= MAX_CACHE_MPZ_LIMBS) { + + global.gmpyxmpzcache[(global.in_gmpyxmpzcache)++] = self; } else { - mpz_clear(obj->z); - PyObject_Del((PyObject*)obj); + mpz_clear(self->z); + PyObject_Del((PyObject*)self); } } /* Caching logic for Pympq. */ -static void -set_gmpympqcache(void) -{ - if (global.in_gmpympqcache > global.cache_size) { - int i; - for (i = global.cache_size; i < global.in_gmpympqcache; ++i) { - mpq_clear(global.gmpympqcache[i]->q); - PyObject_Del(global.gmpympqcache[i]); - } - global.in_gmpympqcache = global.cache_size; - } - global.gmpympqcache = realloc(global.gmpympqcache, sizeof(MPQ_Object)*global.cache_size); -} - static MPQ_Object * GMPy_MPQ_New(CTXT_Object *context) { @@ -381,15 +335,13 @@ GMPy_MPQ_New(CTXT_Object *context) if (global.in_gmpympqcache) { result = global.gmpympqcache[--(global.in_gmpympqcache)]; - /* Py_INCREF does not set the debugging pointers, so need to use - _Py_NewReference instead. */ - _Py_NewReference((PyObject*)result); + Py_INCREF((PyObject*)result); + mpq_set_ui(result->q, 0, 1); } else { - if (!(result = PyObject_New(MPQ_Object, &MPQ_Type))) { - /* LCOV_EXCL_START */ + result = PyObject_New(MPQ_Object, &MPQ_Type); + if (result == NULL) { return NULL; - /* LCOV_EXCL_STOP */ } mpq_init(result->q); } @@ -493,9 +445,10 @@ GMPy_MPQ_NewInit(PyTypeObject *type, PyObject *args, PyObject *keywds) static void GMPy_MPQ_Dealloc(MPQ_Object *self) { - if (global.in_gmpympqcacheq)->_mp_alloc <= global.cache_obsize && - mpq_denref(self->q)->_mp_alloc <= global.cache_obsize) { + if (global.in_gmpympqcache < CACHE_SIZE && + mpq_numref(self->q)->_mp_alloc <= MAX_CACHE_MPZ_LIMBS && + mpq_denref(self->q)->_mp_alloc <= MAX_CACHE_MPZ_LIMBS) { + global.gmpympqcache[(global.in_gmpympqcache)++] = self; } else { @@ -506,20 +459,6 @@ GMPy_MPQ_Dealloc(MPQ_Object *self) /* Caching logic for Pympfr. */ -static void -set_gmpympfrcache(void) -{ - if (global.in_gmpympfrcache > global.cache_size) { - int i; - for (i = global.cache_size; i < global.in_gmpympfrcache; ++i) { - mpfr_clear(global.gmpympfrcache[i]->f); - PyObject_Del(global.gmpympfrcache[i]); - } - global.in_gmpympfrcache = global.cache_size; - } - global.gmpympfrcache = realloc(global.gmpympfrcache, sizeof(MPFR_Object)*global.cache_size); -} - static MPFR_Object * GMPy_MPFR_New(mpfr_prec_t bits, CTXT_Object *context) { @@ -537,19 +476,15 @@ GMPy_MPFR_New(mpfr_prec_t bits, CTXT_Object *context) if (global.in_gmpympfrcache) { result = global.gmpympfrcache[--(global.in_gmpympfrcache)]; - /* Py_INCREF does not set the debugging pointers, so need to use - _Py_NewReference instead. */ - _Py_NewReference((PyObject*)result); - mpfr_set_prec(result->f, bits); + Py_INCREF((PyObject*)result); } else { - if (!(result = PyObject_New(MPFR_Object, &MPFR_Type))) { - /* LCOV_EXCL_START */ + result = PyObject_New(MPFR_Object, &MPFR_Type); + if (result == NULL) { return NULL; - /* LCOV_EXCL_STOP */ } - mpfr_init2(result->f, bits); } + mpfr_init2(result->f, bits); result->hash_cache = -1; result->rc = 0; return result; @@ -672,12 +607,9 @@ GMPy_MPFR_NewInit(PyTypeObject *type, PyObject *args, PyObject *keywds) static void GMPy_MPFR_Dealloc(MPFR_Object *self) { - size_t msize; + if (global.in_gmpympfrcache < CACHE_SIZE && + self->f->_mpfr_prec <= MAX_CACHE_MPFR_BITS) { - /* Calculate the number of limbs in the mantissa. */ - msize = (self->f->_mpfr_prec + mp_bits_per_limb - 1) / mp_bits_per_limb; - if (global.in_gmpympfrcache < global.cache_size && - msize <= (size_t)global.cache_obsize) { global.gmpympfrcache[(global.in_gmpympfrcache)++] = self; } else { @@ -686,25 +618,10 @@ GMPy_MPFR_Dealloc(MPFR_Object *self) } } -static void -set_gmpympccache(void) -{ - if (global.in_gmpympccache > global.cache_size) { - int i; - for (i = global.cache_size; i < global.in_gmpympccache; ++i) { - mpc_clear(global.gmpympccache[i]->c); - PyObject_Del(global.gmpympccache[i]); - } - global.in_gmpympccache = global.cache_size; - } - global.gmpympccache = realloc(global.gmpympccache, sizeof(MPC_Object)*global.cache_size); -} - - static MPC_Object * GMPy_MPC_New(mpfr_prec_t rprec, mpfr_prec_t iprec, CTXT_Object *context) { - MPC_Object *self; + MPC_Object *result; if (rprec < 2) { CHECK_CONTEXT(context); @@ -722,29 +639,19 @@ GMPy_MPC_New(mpfr_prec_t rprec, mpfr_prec_t iprec, CTXT_Object *context) return NULL; } if (global.in_gmpympccache) { - self = global.gmpympccache[--(global.in_gmpympccache)]; - /* Py_INCREF does not set the debugging pointers, so need to use - _Py_NewReference instead. */ - _Py_NewReference((PyObject*)self); - if (rprec == iprec) { - mpc_set_prec(self->c, rprec); - } - else { - mpc_clear(self->c); - mpc_init3(self->c, rprec, iprec); - } + result = global.gmpympccache[--(global.in_gmpympccache)]; + Py_INCREF((PyObject*)result); } else { - if (!(self = PyObject_New(MPC_Object, &MPC_Type))) { - /* LCOV_EXCL_START */ + result = PyObject_New(MPC_Object, &MPC_Type); + if (result == NULL) { return NULL; - /* LCOV_EXCL_STOP */ } - mpc_init3(self->c, rprec, iprec); } - self->hash_cache = -1; - self->rc = 0; - return self; + mpc_init3(result->c, rprec, iprec); + result->hash_cache = -1; + result->rc = 0; + return result; } static PyObject * @@ -974,13 +881,10 @@ GMPy_MPC_NewInit(PyTypeObject *type, PyObject *args, PyObject *keywds) static void GMPy_MPC_Dealloc(MPC_Object *self) { - size_t msize; + if (global.in_gmpympccache < CACHE_SIZE && + mpc_realref(self->c)->_mpfr_prec <= MAX_CACHE_MPFR_BITS && + mpc_imagref(self->c)->_mpfr_prec <= MAX_CACHE_MPFR_BITS) { - /* Calculate the number of limbs in the mantissa. */ - msize = (mpc_realref(self->c)->_mpfr_prec + mp_bits_per_limb - 1) / mp_bits_per_limb; - msize += (mpc_imagref(self->c)->_mpfr_prec + mp_bits_per_limb - 1) / mp_bits_per_limb; - if (global.in_gmpympccache < global.cache_size && - msize <= (size_t)global.cache_obsize) { global.gmpympccache[(global.in_gmpympccache)++] = self; } else { diff --git a/src/gmpy2_cache.h b/src/gmpy2_cache.h index 81ed6cd2..6562c3b9 100644 --- a/src/gmpy2_cache.h +++ b/src/gmpy2_cache.h @@ -38,12 +38,6 @@ extern "C" { /* Private functions */ -static void set_gmpympzcache(void); -static void set_gmpyxmpzcache(void); -static void set_gmpympqcache(void); -static void set_gmpympfrcache(void); -static void set_gmpympccache(void); - /* C-API functions */ /* static MPZ_Object * GMPy_MPZ_New(CTXT_Object *context); */ diff --git a/src/gmpy2_misc.c b/src/gmpy2_misc.c index 70ee9da8..66bf7093 100644 --- a/src/gmpy2_misc.c +++ b/src/gmpy2_misc.c @@ -91,53 +91,6 @@ GMPy_get_mp_limbsize(PyObject *self, PyObject *args) return Py_BuildValue("i", mp_bits_per_limb); } -/* - * access cache options - */ - -PyDoc_STRVAR(GMPy_doc_get_cache, -"get_cache() -> (cache_size, object_size)\n\n\ -Return the current cache size (number of objects) and maximum size\n\ -per object (number of limbs) for all GMPY2 objects."); - -static PyObject * -GMPy_get_cache(PyObject *self, PyObject *args) -{ - return Py_BuildValue("(ii)", global.cache_size, global.cache_obsize); -} - -PyDoc_STRVAR(GMPy_doc_set_cache, -"set_cache(cache_size, object_size)\n\n\ -Set the current cache size (number of objects) and the maximum size\n\ -per object (number of limbs). Raises ValueError if cache size exceeds\n\ -1000 or object size exceeds 16384."); - -static PyObject * -GMPy_set_cache(PyObject *self, PyObject *args) -{ - int newcache = -1, newsize = -1; - - if (!PyArg_ParseTuple(args, "ii", &newcache, &newsize)) - return NULL; - if (newcache<0 || newcache>MAX_CACHE) { - VALUE_ERROR("cache size must between 0 and 1000"); - return NULL; - } - if (newsize<0 || newsize>MAX_CACHE_LIMBS) { - VALUE_ERROR("object size must between 0 and 16384"); - return NULL; - } - - global.cache_size = newcache; - global.cache_obsize = newsize; - set_gmpympzcache(); - set_gmpympqcache(); - set_gmpyxmpzcache(); - set_gmpympfrcache(); - set_gmpympccache(); - Py_RETURN_NONE; -} - PyDoc_STRVAR(GMPy_doc_function_printf, "_printf(fmt, x) -> string\n\n" "Return a Python string by formatting 'x' using the format string\n" diff --git a/src/gmpy2_misc.h b/src/gmpy2_misc.h index f8d88f28..c3de6a20 100644 --- a/src/gmpy2_misc.h +++ b/src/gmpy2_misc.h @@ -37,8 +37,6 @@ static PyObject * GMPy_get_mp_version(PyObject *self, PyObject *args); static PyObject * GMPy_get_mpfr_version(PyObject *self, PyObject *args); static PyObject * GMPy_get_mpc_version(PyObject *self, PyObject *args); static PyObject * GMPy_get_mp_limbsize(PyObject *self, PyObject *args); -static PyObject * GMPy_get_cache(PyObject *self, PyObject *args); -static PyObject * GMPy_set_cache(PyObject *self, PyObject *args); static PyObject * GMPy_printf(PyObject *self, PyObject *args); #ifdef __cplusplus diff --git a/test/runtests.py b/test/runtests.py index 260ba036..88ff484a 100644 --- a/test/runtests.py +++ b/test/runtests.py @@ -71,8 +71,6 @@ def parse(self, *args, **kwargs): print(" Mutliple-precision library: {0}".format(gmpy2.mp_version())) print(" Floating-point library: {0}".format(gmpy2.mpfr_version())) print(" Complex library: {0}".format(gmpy2.mpc_version())) -print(" Caching Values: (Cache size) {0}".format(gmpy2.get_cache()[0])) -print(" Caching Values: (Size in limbs) {0}".format(gmpy2.get_cache()[1])) print() mpz_doctests = ["test_mpz_create.txt", "test_mpz.txt", "test_mpz_io.txt", diff --git a/test/test_misc.txt b/test/test_misc.txt index 8c5a9332..12fab3fe 100644 --- a/test/test_misc.txt +++ b/test/test_misc.txt @@ -24,38 +24,3 @@ True >>> gmpy2.license() 'The GMPY2 source code is licensed under LGPL 3 or later. The supported versions of the GMP, MPFR, and MPC libraries are also licensed under LGPL 3 or later.' ->>> gmpy2.get_cache() -(100, 128) ->>> gmpy2.divm(5,7,13) -mpz(10) ->>> gmpy2.set_cache(0,16) ->>> gmpy2.get_cache() -(0, 16) ->>> for i in range(1000): a=mpz(i) ->>> del(a) ->>> gmpy2.set_cache(100,128) ->>> gmpy2.get_cache() -(100, 128) ->>> gmpy2.set_cache('a', 128) -Traceback (most recent call last): - File "", line 1, in -TypeError: ** message detail varies ** ->>> gmpy2.set_cache(-10, 128) -Traceback (most recent call last): - File "", line 1, in -ValueError: ** message detail varies ** ->>> gmpy2.set_cache(10000, 128) -Traceback (most recent call last): - File "", line 1, in -ValueError: ** message detail varies ** ->>> gmpy2.set_cache(100, -1) -Traceback (most recent call last): - File "", line 1, in -ValueError: ** message detail varies ** ->>> gmpy2.set_cache(100, 100000) -Traceback (most recent call last): - File "", line 1, in -ValueError: ** message detail varies ** ->>> 1 -1 -