[Python-checkins] bpo-45522: Allow to disable freelists on build time (GH-29056)

miss-islington webhook-mailer at python.org
Thu Oct 21 09:12:30 EDT 2021


https://github.com/python/cpython/commit/9942f42a93ccda047fd3558c47b822e99afe10c0
commit: 9942f42a93ccda047fd3558c47b822e99afe10c0
branch: main
author: Christian Heimes <christian at python.org>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-10-21T06:12:20-07:00
summary:

bpo-45522: Allow to disable freelists on build time (GH-29056)



Freelists for object structs can now be disabled. A new ``configure``
option ``--without-freelists`` can be used to disable all freelists
except empty tuple singleton. Internal Py*_MAXFREELIST macros can now
be defined as 0 without causing compiler warnings and segfaults.

Signed-off-by: Christian Heimes <christian at python.org>

files:
A Misc/NEWS.d/next/C API/2021-10-19-13-07-46.bpo-45522.kGAwmZ.rst
M Doc/whatsnew/3.11.rst
M Include/internal/pycore_interp.h
M Lib/test/test_sys.py
M Objects/dictobject.c
M Objects/floatobject.c
M Objects/frameobject.c
M Objects/genobject.c
M Objects/listobject.c
M Objects/tupleobject.c
M PC/pyconfig.h
M Python/context.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index d5e8bc967a816..74fc7536ea231 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -481,6 +481,11 @@ Build Changes
   ``isinf()``, ``isnan()``, ``round()``.
   (Contributed by Victor Stinner in :issue:`45440`.)
 
+* Freelists for object structs can now be disabled. A new :program:`configure`
+  option :option:`!--without-freelists` can be used to disable all freelists
+  except empty tuple singleton.
+  (Contributed by Christian Heimes in :issue:`45522`)
+
 C API Changes
 =============
 
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 0e6edf4ec2670..64ac3abe00fa0 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -85,12 +85,31 @@ struct _Py_unicode_state {
     struct _Py_unicode_ids ids;
 };
 
+#ifndef WITH_FREELISTS
+// without freelists
+#  define PyFloat_MAXFREELIST 0
+// for tuples only store empty tuple singleton
+#  define PyTuple_MAXSAVESIZE 1
+#  define PyTuple_MAXFREELIST 1
+#  define PyList_MAXFREELIST 0
+#  define PyDict_MAXFREELIST 0
+#  define PyFrame_MAXFREELIST 0
+#  define _PyAsyncGen_MAXFREELIST 0
+#  define PyContext_MAXFREELIST 0
+#endif
+
+#ifndef PyFloat_MAXFREELIST
+#  define PyFloat_MAXFREELIST   100
+#endif
+
 struct _Py_float_state {
+#if PyFloat_MAXFREELIST > 0
     /* Special free list
        free_list is a singly-linked list of available PyFloatObjects,
        linked via abuse of their ob_type members. */
     int numfree;
     PyFloatObject *free_list;
+#endif
 };
 
 /* Speed optimization to avoid frequent malloc/free of small tuples */
@@ -119,8 +138,10 @@ struct _Py_tuple_state {
 #endif
 
 struct _Py_list_state {
+#if PyList_MAXFREELIST > 0
     PyListObject *free_list[PyList_MAXFREELIST];
     int numfree;
+#endif
 };
 
 #ifndef PyDict_MAXFREELIST
@@ -128,17 +149,25 @@ struct _Py_list_state {
 #endif
 
 struct _Py_dict_state {
+#if PyDict_MAXFREELIST > 0
     /* Dictionary reuse scheme to save calls to malloc and free */
     PyDictObject *free_list[PyDict_MAXFREELIST];
     int numfree;
     PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
     int keys_numfree;
+#endif
 };
 
+#ifndef PyFrame_MAXFREELIST
+#  define PyFrame_MAXFREELIST 200
+#endif
+
 struct _Py_frame_state {
+#if PyFrame_MAXFREELIST > 0
     PyFrameObject *free_list;
     /* number of frames currently in free_list */
     int numfree;
+#endif
 };
 
 #ifndef _PyAsyncGen_MAXFREELIST
@@ -146,6 +175,7 @@ struct _Py_frame_state {
 #endif
 
 struct _Py_async_gen_state {
+#if _PyAsyncGen_MAXFREELIST > 0
     /* Freelists boost performance 6-10%; they also reduce memory
        fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend
        are short-living objects that are instantiated for every
@@ -155,12 +185,19 @@ struct _Py_async_gen_state {
 
     struct PyAsyncGenASend* asend_freelist[_PyAsyncGen_MAXFREELIST];
     int asend_numfree;
+#endif
 };
 
+#ifndef PyContext_MAXFREELIST
+#  define PyContext_MAXFREELIST 255
+#endif
+
 struct _Py_context_state {
+#if PyContext_MAXFREELIST > 0
     // List of free PyContext objects
     PyContext *freelist;
     int numfree;
+#endif
 };
 
 struct _Py_exc_state {
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 2182898837e50..b0688e1e605fe 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -825,7 +825,18 @@ def test_debugmallocstats(self):
         from test.support.script_helper import assert_python_ok
         args = ['-c', 'import sys; sys._debugmallocstats()']
         ret, out, err = assert_python_ok(*args)
-        self.assertIn(b"free PyDictObjects", err)
+
+        # Output of sys._debugmallocstats() depends on configure flags.
+        # The sysconfig vars are not available on Windows.
+        if sys.platform != "win32":
+            with_freelists = sysconfig.get_config_var("WITH_FREELISTS")
+            with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
+            if with_freelists:
+                self.assertIn(b"free PyDictObjects", err)
+            if with_pymalloc:
+                self.assertIn(b'Small block threshold', err)
+            if not with_freelists and not with_pymalloc:
+                self.assertFalse(err)
 
         # The function has no parameter
         self.assertRaises(TypeError, sys._debugmallocstats, True)
diff --git a/Misc/NEWS.d/next/C API/2021-10-19-13-07-46.bpo-45522.kGAwmZ.rst b/Misc/NEWS.d/next/C API/2021-10-19-13-07-46.bpo-45522.kGAwmZ.rst
new file mode 100644
index 0000000000000..658261f8ce693
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2021-10-19-13-07-46.bpo-45522.kGAwmZ.rst	
@@ -0,0 +1,2 @@
+The internal freelists for frame, float, list, dict, async generators, and
+context objects can now be disabled.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 3d6e4c1e17e1f..6dcd5a1d19715 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -240,17 +240,20 @@ uint64_t _pydict_global_version = 0;
 #include "clinic/dictobject.c.h"
 
 
+#if PyDict_MAXFREELIST > 0
 static struct _Py_dict_state *
 get_dict_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->dict_state;
 }
+#endif
 
 
 void
 _PyDict_ClearFreeList(PyInterpreterState *interp)
 {
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = &interp->dict_state;
     while (state->numfree) {
         PyDictObject *op = state->free_list[--state->numfree];
@@ -260,6 +263,7 @@ _PyDict_ClearFreeList(PyInterpreterState *interp)
     while (state->keys_numfree) {
         PyObject_Free(state->keys_free_list[--state->keys_numfree]);
     }
+#endif
 }
 
 
@@ -267,7 +271,7 @@ void
 _PyDict_Fini(PyInterpreterState *interp)
 {
     _PyDict_ClearFreeList(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = &interp->dict_state;
     state->numfree = -1;
     state->keys_numfree = -1;
@@ -279,9 +283,11 @@ _PyDict_Fini(PyInterpreterState *interp)
 void
 _PyDict_DebugMallocStats(FILE *out)
 {
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = get_dict_state();
     _PyDebugAllocatorStats(out, "free PyDictObject",
                            state->numfree, sizeof(PyDictObject));
+#endif
 }
 
 #define DK_MASK(dk) (DK_SIZE(dk)-1)
@@ -570,6 +576,7 @@ new_keys_object(uint8_t log2_size)
         es = sizeof(Py_ssize_t);
     }
 
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = get_dict_state();
 #ifdef Py_DEBUG
     // new_keys_object() must not be called after _PyDict_Fini()
@@ -579,6 +586,7 @@ new_keys_object(uint8_t log2_size)
         dk = state->keys_free_list[--state->keys_numfree];
     }
     else
+#endif
     {
         dk = PyObject_Malloc(sizeof(PyDictKeysObject)
                              + (es<<log2_size)
@@ -611,6 +619,7 @@ free_keys_object(PyDictKeysObject *keys)
         Py_XDECREF(entries[i].me_key);
         Py_XDECREF(entries[i].me_value);
     }
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = get_dict_state();
 #ifdef Py_DEBUG
     // free_keys_object() must not be called after _PyDict_Fini()
@@ -620,6 +629,7 @@ free_keys_object(PyDictKeysObject *keys)
         state->keys_free_list[state->keys_numfree++] = keys;
         return;
     }
+#endif
     PyObject_Free(keys);
 }
 
@@ -638,6 +648,7 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values, Py_ssize_t used, int free
 {
     PyDictObject *mp;
     assert(keys != NULL);
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = get_dict_state();
 #ifdef Py_DEBUG
     // new_dict() must not be called after _PyDict_Fini()
@@ -649,7 +660,9 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values, Py_ssize_t used, int free
         assert (Py_IS_TYPE(mp, &PyDict_Type));
         _Py_NewReference((PyObject *)mp);
     }
-    else {
+    else
+#endif
+    {
         mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
         if (mp == NULL) {
             dictkeys_decref(keys);
@@ -1259,6 +1272,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize)
 #ifdef Py_REF_DEBUG
         _Py_RefTotal--;
 #endif
+#if PyDict_MAXFREELIST > 0
         struct _Py_dict_state *state = get_dict_state();
 #ifdef Py_DEBUG
         // dictresize() must not be called after _PyDict_Fini()
@@ -1269,7 +1283,9 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize)
         {
             state->keys_free_list[state->keys_numfree++] = oldkeys;
         }
-        else {
+        else
+#endif
+        {
             PyObject_Free(oldkeys);
         }
     }
@@ -1987,6 +2003,7 @@ dict_dealloc(PyDictObject *mp)
         assert(keys->dk_refcnt == 1);
         dictkeys_decref(keys);
     }
+#if PyDict_MAXFREELIST > 0
     struct _Py_dict_state *state = get_dict_state();
 #ifdef Py_DEBUG
     // new_dict() must not be called after _PyDict_Fini()
@@ -1995,7 +2012,9 @@ dict_dealloc(PyDictObject *mp)
     if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
         state->free_list[state->numfree++] = mp;
     }
-    else {
+    else
+#endif
+    {
         Py_TYPE(mp)->tp_free((PyObject *)mp);
     }
     Py_TRASHCAN_END
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 1be31e38d4935..7fc192e720117 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -28,12 +28,14 @@ class float "PyObject *" "&PyFloat_Type"
 #endif
 
 
+#if PyFloat_MAXFREELIST > 0
 static struct _Py_float_state *
 get_float_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->float_state;
 }
+#endif
 
 
 double
@@ -126,8 +128,10 @@ PyFloat_GetInfo(void)
 PyObject *
 PyFloat_FromDouble(double fval)
 {
+    PyFloatObject *op;
+#if PyFloat_MAXFREELIST > 0
     struct _Py_float_state *state = get_float_state();
-    PyFloatObject *op = state->free_list;
+    op = state->free_list;
     if (op != NULL) {
 #ifdef Py_DEBUG
         // PyFloat_FromDouble() must not be called after _PyFloat_Fini()
@@ -136,7 +140,9 @@ PyFloat_FromDouble(double fval)
         state->free_list = (PyFloatObject *) Py_TYPE(op);
         state->numfree--;
     }
-    else {
+    else
+#endif
+    {
         op = PyObject_Malloc(sizeof(PyFloatObject));
         if (!op) {
             return PyErr_NoMemory();
@@ -233,6 +239,7 @@ PyFloat_FromString(PyObject *v)
 static void
 float_dealloc(PyFloatObject *op)
 {
+#if PyFloat_MAXFREELIST > 0
     if (PyFloat_CheckExact(op)) {
         struct _Py_float_state *state = get_float_state();
 #ifdef Py_DEBUG
@@ -247,7 +254,9 @@ float_dealloc(PyFloatObject *op)
         Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
         state->free_list = op;
     }
-    else {
+    else
+#endif
+    {
         Py_TYPE(op)->tp_free((PyObject *)op);
     }
 }
@@ -2036,6 +2045,7 @@ _PyFloat_InitTypes(void)
 void
 _PyFloat_ClearFreeList(PyInterpreterState *interp)
 {
+#if PyFloat_MAXFREELIST > 0
     struct _Py_float_state *state = &interp->float_state;
     PyFloatObject *f = state->free_list;
     while (f != NULL) {
@@ -2045,13 +2055,14 @@ _PyFloat_ClearFreeList(PyInterpreterState *interp)
     }
     state->free_list = NULL;
     state->numfree = 0;
+#endif
 }
 
 void
 _PyFloat_Fini(PyInterpreterState *interp)
 {
     _PyFloat_ClearFreeList(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && PyFloat_MAXFREELIST > 0
     struct _Py_float_state *state = &interp->float_state;
     state->numfree = -1;
 #endif
@@ -2061,10 +2072,12 @@ _PyFloat_Fini(PyInterpreterState *interp)
 void
 _PyFloat_DebugMallocStats(FILE *out)
 {
+#if PyFloat_MAXFREELIST > 0
     struct _Py_float_state *state = get_float_state();
     _PyDebugAllocatorStats(out,
                            "free PyFloatObject",
                            state->numfree, sizeof(PyFloatObject));
+#endif
 }
 
 
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 5271790f018af..ffe19b3d99400 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -19,12 +19,14 @@ static PyMemberDef frame_memberlist[] = {
     {NULL}      /* Sentinel */
 };
 
+#if PyFrame_MAXFREELIST > 0
 static struct _Py_frame_state *
 get_frame_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->frame;
 }
+#endif
 
 
 static PyObject *
@@ -607,9 +609,6 @@ static PyGetSetDef frame_getsetlist[] = {
     f_back              next item on free list, or NULL
 */
 
-/* max value for numfree */
-#define PyFrame_MAXFREELIST 200
-
 static void _Py_HOT_FUNCTION
 frame_dealloc(PyFrameObject *f)
 {
@@ -638,6 +637,7 @@ frame_dealloc(PyFrameObject *f)
     }
     Py_CLEAR(f->f_back);
     Py_CLEAR(f->f_trace);
+#if PyFrame_MAXFREELIST > 0
     struct _Py_frame_state *state = get_frame_state();
 #ifdef Py_DEBUG
     // frame_dealloc() must not be called after _PyFrame_Fini()
@@ -648,7 +648,9 @@ frame_dealloc(PyFrameObject *f)
         f->f_back = state->free_list;
         state->free_list = f;
     }
-    else {
+    else
+#endif
+    {
         PyObject_GC_Del(f);
     }
 
@@ -801,8 +803,10 @@ static inline PyFrameObject*
 frame_alloc(InterpreterFrame *frame, int owns)
 {
     PyFrameObject *f;
+#if PyFrame_MAXFREELIST > 0
     struct _Py_frame_state *state = get_frame_state();
     if (state->free_list == NULL)
+#endif
     {
         f = PyObject_GC_New(PyFrameObject, &PyFrame_Type);
         if (f == NULL) {
@@ -816,7 +820,9 @@ frame_alloc(InterpreterFrame *frame, int owns)
             return NULL;
         }
     }
-    else {
+#if PyFrame_MAXFREELIST > 0
+    else
+    {
 #ifdef Py_DEBUG
         // frame_alloc() must not be called after _PyFrame_Fini()
         assert(state->numfree != -1);
@@ -827,6 +833,7 @@ frame_alloc(InterpreterFrame *frame, int owns)
         state->free_list = state->free_list->f_back;
         _Py_NewReference((PyObject *)f);
     }
+#endif
     f->f_frame = frame;
     f->f_own_locals_memory = owns;
     return f;
@@ -1069,6 +1076,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
 void
 _PyFrame_ClearFreeList(PyInterpreterState *interp)
 {
+#if PyFrame_MAXFREELIST > 0
     struct _Py_frame_state *state = &interp->frame;
     while (state->free_list != NULL) {
         PyFrameObject *f = state->free_list;
@@ -1077,13 +1085,14 @@ _PyFrame_ClearFreeList(PyInterpreterState *interp)
         --state->numfree;
     }
     assert(state->numfree == 0);
+#endif
 }
 
 void
 _PyFrame_Fini(PyInterpreterState *interp)
 {
     _PyFrame_ClearFreeList(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && PyFrame_MAXFREELIST > 0
     struct _Py_frame_state *state = &interp->frame;
     state->numfree = -1;
 #endif
@@ -1093,10 +1102,12 @@ _PyFrame_Fini(PyInterpreterState *interp)
 void
 _PyFrame_DebugMallocStats(FILE *out)
 {
+#if PyFrame_MAXFREELIST > 0
     struct _Py_frame_state *state = get_frame_state();
     _PyDebugAllocatorStats(out,
                            "free PyFrameObject",
                            state->numfree, sizeof(PyFrameObject));
+#endif
 }
 
 
diff --git a/Objects/genobject.c b/Objects/genobject.c
index f91f367f9a687..8bf55155eab1c 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1567,12 +1567,14 @@ PyTypeObject PyAsyncGen_Type = {
 };
 
 
+#if _PyAsyncGen_MAXFREELIST > 0
 static struct _Py_async_gen_state *
 get_async_gen_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->async_gen;
 }
+#endif
 
 
 PyObject *
@@ -1595,6 +1597,7 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
 void
 _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp)
 {
+#if _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = &interp->async_gen;
 
     while (state->value_numfree) {
@@ -1610,13 +1613,14 @@ _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp)
         assert(Py_IS_TYPE(o, &_PyAsyncGenASend_Type));
         PyObject_GC_Del(o);
     }
+#endif
 }
 
 void
 _PyAsyncGen_Fini(PyInterpreterState *interp)
 {
     _PyAsyncGen_ClearFreeLists(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = &interp->async_gen;
     state->value_numfree = -1;
     state->asend_numfree = -1;
@@ -1663,6 +1667,7 @@ async_gen_asend_dealloc(PyAsyncGenASend *o)
     _PyObject_GC_UNTRACK((PyObject *)o);
     Py_CLEAR(o->ags_gen);
     Py_CLEAR(o->ags_sendval);
+#if _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = get_async_gen_state();
 #ifdef Py_DEBUG
     // async_gen_asend_dealloc() must not be called after _PyAsyncGen_Fini()
@@ -1672,7 +1677,9 @@ async_gen_asend_dealloc(PyAsyncGenASend *o)
         assert(PyAsyncGenASend_CheckExact(o));
         state->asend_freelist[state->asend_numfree++] = o;
     }
-    else {
+    else
+#endif
+    {
         PyObject_GC_Del(o);
     }
 }
@@ -1825,6 +1832,7 @@ static PyObject *
 async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval)
 {
     PyAsyncGenASend *o;
+#if _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = get_async_gen_state();
 #ifdef Py_DEBUG
     // async_gen_asend_new() must not be called after _PyAsyncGen_Fini()
@@ -1835,7 +1843,9 @@ async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval)
         o = state->asend_freelist[state->asend_numfree];
         _Py_NewReference((PyObject *)o);
     }
-    else {
+    else
+#endif
+    {
         o = PyObject_GC_New(PyAsyncGenASend, &_PyAsyncGenASend_Type);
         if (o == NULL) {
             return NULL;
@@ -1863,6 +1873,7 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
 {
     _PyObject_GC_UNTRACK((PyObject *)o);
     Py_CLEAR(o->agw_val);
+#if _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = get_async_gen_state();
 #ifdef Py_DEBUG
     // async_gen_wrapped_val_dealloc() must not be called after _PyAsyncGen_Fini()
@@ -1872,7 +1883,9 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
         assert(_PyAsyncGenWrappedValue_CheckExact(o));
         state->value_freelist[state->value_numfree++] = o;
     }
-    else {
+    else
+#endif
+    {
         PyObject_GC_Del(o);
     }
 }
@@ -1936,6 +1949,7 @@ _PyAsyncGenValueWrapperNew(PyObject *val)
     _PyAsyncGenWrappedValue *o;
     assert(val);
 
+#if _PyAsyncGen_MAXFREELIST > 0
     struct _Py_async_gen_state *state = get_async_gen_state();
 #ifdef Py_DEBUG
     // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini()
@@ -1947,7 +1961,9 @@ _PyAsyncGenValueWrapperNew(PyObject *val)
         assert(_PyAsyncGenWrappedValue_CheckExact(o));
         _Py_NewReference((PyObject*)o);
     }
-    else {
+    else
+#endif
+    {
         o = PyObject_GC_New(_PyAsyncGenWrappedValue,
                             &_PyAsyncGenWrappedValue_Type);
         if (o == NULL) {
diff --git a/Objects/listobject.c b/Objects/listobject.c
index ed5324155f627..e0cae87f45781 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -19,13 +19,14 @@ class list "PyListObject *" "&PyList_Type"
 
 #include "clinic/listobject.c.h"
 
-
+#if PyList_MAXFREELIST > 0
 static struct _Py_list_state *
 get_list_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->list;
 }
+#endif
 
 
 /* Ensure ob_item has room for at least newsize elements, and set
@@ -108,19 +109,21 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
 void
 _PyList_ClearFreeList(PyInterpreterState *interp)
 {
+#if PyList_MAXFREELIST > 0
     struct _Py_list_state *state = &interp->list;
     while (state->numfree) {
         PyListObject *op = state->free_list[--state->numfree];
         assert(PyList_CheckExact(op));
         PyObject_GC_Del(op);
     }
+#endif
 }
 
 void
 _PyList_Fini(PyInterpreterState *interp)
 {
     _PyList_ClearFreeList(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && PyList_MAXFREELIST > 0
     struct _Py_list_state *state = &interp->list;
     state->numfree = -1;
 #endif
@@ -130,32 +133,38 @@ _PyList_Fini(PyInterpreterState *interp)
 void
 _PyList_DebugMallocStats(FILE *out)
 {
+#if PyList_MAXFREELIST > 0
     struct _Py_list_state *state = get_list_state();
     _PyDebugAllocatorStats(out,
                            "free PyListObject",
                            state->numfree, sizeof(PyListObject));
+#endif
 }
 
 PyObject *
 PyList_New(Py_ssize_t size)
 {
+    PyListObject *op;
+
     if (size < 0) {
         PyErr_BadInternalCall();
         return NULL;
     }
 
+#if PyList_MAXFREELIST > 0
     struct _Py_list_state *state = get_list_state();
-    PyListObject *op;
 #ifdef Py_DEBUG
     // PyList_New() must not be called after _PyList_Fini()
     assert(state->numfree != -1);
 #endif
-    if (state->numfree) {
+    if (PyList_MAXFREELIST && state->numfree) {
         state->numfree--;
         op = state->free_list[state->numfree];
         _Py_NewReference((PyObject *)op);
     }
-    else {
+    else
+#endif
+    {
         op = PyObject_GC_New(PyListObject, &PyList_Type);
         if (op == NULL) {
             return NULL;
@@ -344,6 +353,7 @@ list_dealloc(PyListObject *op)
         }
         PyMem_Free(op->ob_item);
     }
+#if PyList_MAXFREELIST > 0
     struct _Py_list_state *state = get_list_state();
 #ifdef Py_DEBUG
     // list_dealloc() must not be called after _PyList_Fini()
@@ -352,7 +362,9 @@ list_dealloc(PyListObject *op)
     if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
         state->free_list[state->numfree++] = op;
     }
-    else {
+    else
+#endif
+    {
         Py_TYPE(op)->tp_free((PyObject *)op);
     }
     Py_TRASHCAN_END
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 051683086ea2c..e9d1b5926abb3 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -66,7 +66,8 @@ tuple_alloc(Py_ssize_t size)
         return NULL;
     }
 
-#if PyTuple_MAXSAVESIZE > 0
+// Check for max save size > 1. Empty tuple singleton is special case.
+#if PyTuple_MAXSAVESIZE > 1
     struct _Py_tuple_state *state = get_tuple_state();
 #ifdef Py_DEBUG
     // tuple_alloc() must not be called after _PyTuple_Fini()
diff --git a/PC/pyconfig.h b/PC/pyconfig.h
index bb55ff4fe14c4..b3e73d4dabefe 100644
--- a/PC/pyconfig.h
+++ b/PC/pyconfig.h
@@ -463,6 +463,9 @@ Py_NO_ENABLE_SHARED to find out.  Also support MS_NO_COREDLL for b/w compat */
 /* Use Python's own small-block memory-allocator. */
 #define WITH_PYMALLOC 1
 
+/* Define if you want to compile in object freelists optimization */
+#define WITH_FREELISTS 1
+
 /* Define if you have clock.  */
 /* #define HAVE_CLOCK */
 
diff --git a/Python/context.c b/Python/context.c
index d78f7f993bb89..a20ec7123731c 100644
--- a/Python/context.c
+++ b/Python/context.c
@@ -9,9 +9,6 @@
 #include "structmember.h"         // PyMemberDef
 
 
-#define CONTEXT_FREELIST_MAXLEN 255
-
-
 #include "clinic/context.c.h"
 /*[clinic input]
 module _contextvars
@@ -66,12 +63,14 @@ static int
 contextvar_del(PyContextVar *var);
 
 
+#if PyContext_MAXFREELIST > 0
 static struct _Py_context_state *
 get_context_state(void)
 {
     PyInterpreterState *interp = _PyInterpreterState_GET();
     return &interp->context;
 }
+#endif
 
 
 PyObject *
@@ -340,8 +339,9 @@ class _contextvars.Context "PyContext *" "&PyContext_Type"
 static inline PyContext *
 _context_alloc(void)
 {
-    struct _Py_context_state *state = get_context_state();
     PyContext *ctx;
+#if PyContext_MAXFREELIST > 0
+    struct _Py_context_state *state = get_context_state();
 #ifdef Py_DEBUG
     // _context_alloc() must not be called after _PyContext_Fini()
     assert(state->numfree != -1);
@@ -353,7 +353,9 @@ _context_alloc(void)
         ctx->ctx_weakreflist = NULL;
         _Py_NewReference((PyObject *)ctx);
     }
-    else {
+    else
+#endif
+    {
         ctx = PyObject_GC_New(PyContext, &PyContext_Type);
         if (ctx == NULL) {
             return NULL;
@@ -469,17 +471,20 @@ context_tp_dealloc(PyContext *self)
     }
     (void)context_tp_clear(self);
 
+#if PyContext_MAXFREELIST > 0
     struct _Py_context_state *state = get_context_state();
 #ifdef Py_DEBUG
     // _context_alloc() must not be called after _PyContext_Fini()
     assert(state->numfree != -1);
 #endif
-    if (state->numfree < CONTEXT_FREELIST_MAXLEN) {
+    if (state->numfree < PyContext_MAXFREELIST) {
         state->numfree++;
         self->ctx_weakreflist = (PyObject *)state->freelist;
         state->freelist = self;
     }
-    else {
+    else
+#endif
+    {
         Py_TYPE(self)->tp_free(self);
     }
 }
@@ -1289,6 +1294,7 @@ get_token_missing(void)
 void
 _PyContext_ClearFreeList(PyInterpreterState *interp)
 {
+#if PyContext_MAXFREELIST > 0
     struct _Py_context_state *state = &interp->context;
     for (; state->numfree; state->numfree--) {
         PyContext *ctx = state->freelist;
@@ -1296,6 +1302,7 @@ _PyContext_ClearFreeList(PyInterpreterState *interp)
         ctx->ctx_weakreflist = NULL;
         PyObject_GC_Del(ctx);
     }
+#endif
 }
 
 
@@ -1306,7 +1313,7 @@ _PyContext_Fini(PyInterpreterState *interp)
         Py_CLEAR(_token_missing);
     }
     _PyContext_ClearFreeList(interp);
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && PyContext_MAXFREELIST > 0
     struct _Py_context_state *state = &interp->context;
     state->numfree = -1;
 #endif
diff --git a/configure b/configure
index ec7c72c7f3276..198b0703fd6af 100755
--- a/configure
+++ b/configure
@@ -846,6 +846,7 @@ with_dbmliborder
 enable_ipv6
 with_doc_strings
 with_pymalloc
+with_freelists
 with_c_locale_coercion
 with_valgrind
 with_dtrace
@@ -1588,6 +1589,7 @@ Optional Packages:
                           names `ndbm', `gdbm' and `bdb'.
   --with-doc-strings      enable documentation strings (default is yes)
   --with-pymalloc         enable specialized mallocs (default is yes)
+  --with-freelists        enable object freelists (default is yes)
   --with-c-locale-coercion
                           enable C locale coercion to a UTF-8 based locale
                           (default is yes)
@@ -11728,6 +11730,30 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_pymalloc" >&5
 $as_echo "$with_pymalloc" >&6; }
 
+# Check whether objects such as float, tuple and dict are using
+# freelists to optimization memory allocation.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-freelists" >&5
+$as_echo_n "checking for --with-freelists... " >&6; }
+
+# Check whether --with-freelists was given.
+if test "${with_freelists+set}" = set; then :
+  withval=$with_freelists;
+fi
+
+
+if test -z "$with_freelists"
+then
+    with_freelists="yes"
+fi
+if test "$with_freelists" != "no"
+then
+
+$as_echo "#define WITH_FREELISTS 1" >>confdefs.h
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_freelists" >&5
+$as_echo "$with_freelists" >&6; }
+
 # Check for --with-c-locale-coercion
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-c-locale-coercion" >&5
 $as_echo_n "checking for --with-c-locale-coercion... " >&6; }
diff --git a/configure.ac b/configure.ac
index c0259524756f2..edda08daac117 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3616,6 +3616,23 @@ then
 fi
 AC_MSG_RESULT($with_pymalloc)
 
+# Check whether objects such as float, tuple and dict are using
+# freelists to optimization memory allocation.
+AC_MSG_CHECKING(for --with-freelists)
+AC_ARG_WITH(freelists,
+            AS_HELP_STRING([--with-freelists], [enable object freelists (default is yes)]))
+
+if test -z "$with_freelists"
+then
+    with_freelists="yes"
+fi
+if test "$with_freelists" != "no"
+then
+    AC_DEFINE(WITH_FREELISTS, 1,
+     [Define if you want to compile in object freelists optimization])
+fi
+AC_MSG_RESULT($with_freelists)
+
 # Check for --with-c-locale-coercion
 AC_MSG_CHECKING(for --with-c-locale-coercion)
 AC_ARG_WITH(c-locale-coercion,
diff --git a/pyconfig.h.in b/pyconfig.h.in
index a426e8effddb9..081ea61bae834 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -1555,6 +1555,9 @@
 /* Define to build the readline module against Editline. */
 #undef WITH_EDITLINE
 
+/* Define if you want to compile in object freelists optimization */
+#undef WITH_FREELISTS
+
 /* Define to 1 if libintl is needed for locale functions. */
 #undef WITH_LIBINTL
 



More information about the Python-checkins mailing list