``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
=============
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 */
#endif
struct _Py_list_state {
+#if PyList_MAXFREELIST > 0
PyListObject *free_list[PyList_MAXFREELIST];
int numfree;
+#endif
};
#ifndef PyDict_MAXFREELIST
#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
#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
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 {
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)
--- /dev/null
+The internal freelists for frame, float, list, dict, async generators, and
+context objects can now be disabled.
#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];
while (state->keys_numfree) {
PyObject_Free(state->keys_free_list[--state->keys_numfree]);
}
+#endif
}
_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;
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)
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()
dk = state->keys_free_list[--state->keys_numfree];
}
else
+#endif
{
dk = PyObject_Malloc(sizeof(PyDictKeysObject)
+ (es<<log2_size)
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()
state->keys_free_list[state->keys_numfree++] = keys;
return;
}
+#endif
PyObject_Free(keys);
}
{
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()
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);
#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()
{
state->keys_free_list[state->keys_numfree++] = oldkeys;
}
- else {
+ else
+#endif
+ {
PyObject_Free(oldkeys);
}
}
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()
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
#endif
+#if PyFloat_MAXFREELIST > 0
static struct _Py_float_state *
get_float_state(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->float_state;
}
+#endif
double
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()
state->free_list = (PyFloatObject *) Py_TYPE(op);
state->numfree--;
}
- else {
+ else
+#endif
+ {
op = PyObject_Malloc(sizeof(PyFloatObject));
if (!op) {
return PyErr_NoMemory();
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
Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
state->free_list = op;
}
- else {
+ else
+#endif
+ {
Py_TYPE(op)->tp_free((PyObject *)op);
}
}
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) {
}
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
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
}
{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 *
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)
{
}
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()
f->f_back = state->free_list;
state->free_list = f;
}
- else {
+ else
+#endif
+ {
PyObject_GC_Del(f);
}
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) {
return NULL;
}
}
- else {
+#if PyFrame_MAXFREELIST > 0
+ else
+ {
#ifdef Py_DEBUG
// frame_alloc() must not be called after _PyFrame_Fini()
assert(state->numfree != -1);
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;
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;
--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
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
}
};
+#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 *
void
_PyAsyncGen_ClearFreeLists(PyInterpreterState *interp)
{
+#if _PyAsyncGen_MAXFREELIST > 0
struct _Py_async_gen_state *state = &interp->async_gen;
while (state->value_numfree) {
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;
_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()
assert(PyAsyncGenASend_CheckExact(o));
state->asend_freelist[state->asend_numfree++] = o;
}
- else {
+ else
+#endif
+ {
PyObject_GC_Del(o);
}
}
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()
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;
{
_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()
assert(_PyAsyncGenWrappedValue_CheckExact(o));
state->value_freelist[state->value_numfree++] = o;
}
- else {
+ else
+#endif
+ {
PyObject_GC_Del(o);
}
}
_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()
assert(_PyAsyncGenWrappedValue_CheckExact(o));
_Py_NewReference((PyObject*)o);
}
- else {
+ else
+#endif
+ {
o = PyObject_GC_New(_PyAsyncGenWrappedValue,
&_PyAsyncGenWrappedValue_Type);
if (o == NULL) {
#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
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
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;
}
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()
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
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()
/* 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 */
#include "structmember.h" // PyMemberDef
-#define CONTEXT_FREELIST_MAXLEN 255
-
-
#include "clinic/context.c.h"
/*[clinic input]
module _contextvars
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 *
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);
ctx->ctx_weakreflist = NULL;
_Py_NewReference((PyObject *)ctx);
}
- else {
+ else
+#endif
+ {
ctx = PyObject_GC_New(PyContext, &PyContext_Type);
if (ctx == NULL) {
return NULL;
}
(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);
}
}
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;
ctx->ctx_weakreflist = NULL;
PyObject_GC_Del(ctx);
}
+#endif
}
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
enable_ipv6
with_doc_strings
with_pymalloc
+with_freelists
with_c_locale_coercion
with_valgrind
with_dtrace
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)
{ $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; }
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,
/* 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