diff options
author | Victor Stinner <vstinner@python.org> | 2020-06-23 09:33:18 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-23 09:33:18 (GMT) |
commit | b4e85cadfbc2b1b24ec5f3159e351dbacedaa5e0 (patch) | |
tree | a121d0df89bdc965649d65a0ca50543293431e61 /Objects/dictobject.c | |
parent | 26a1ad1c24717990265b71ed093d691500d6301c (diff) | |
download | cpython-b4e85cadfbc2b1b24ec5f3159e351dbacedaa5e0.zip cpython-b4e85cadfbc2b1b24ec5f3159e351dbacedaa5e0.tar.gz cpython-b4e85cadfbc2b1b24ec5f3159e351dbacedaa5e0.tar.bz2 |
bpo-40521: Make dict free lists per-interpreter (GH-20645)
Each interpreter now has its own dict free list:
* Move dict free lists into PyInterpreterState.
* Move PyDict_MAXFREELIST define to pycore_interp.h
* Add _Py_dict_state structure.
* Add tstate parameter to _PyDict_ClearFreeList() and _PyDict_Fini().
* In debug mode, ensure that the dict free lists are not used after
_PyDict_Fini() is called.
* Remove "#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS".
Diffstat (limited to 'Objects/dictobject.c')
-rw-r--r-- | Objects/dictobject.c | 125 |
1 files changed, 66 insertions, 59 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 55bf4ae..f3b1157 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -247,58 +247,47 @@ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) -/* Dictionary reuse scheme to save calls to malloc and free */ -#ifndef PyDict_MAXFREELIST -#define PyDict_MAXFREELIST 80 -#endif - -/* bpo-40521: dict free lists are shared by all interpreters. */ -#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS -# undef PyDict_MAXFREELIST -# define PyDict_MAXFREELIST 0 -#endif - -#if PyDict_MAXFREELIST > 0 -static PyDictObject *free_list[PyDict_MAXFREELIST]; -static int numfree = 0; -static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; -static int numfreekeys = 0; -#endif - #include "clinic/dictobject.c.h" void -_PyDict_ClearFreeList(void) +_PyDict_ClearFreeList(PyThreadState *tstate) { -#if PyDict_MAXFREELIST > 0 - while (numfree) { - PyDictObject *op = free_list[--numfree]; + struct _Py_dict_state *state = &tstate->interp->dict_state; + while (state->numfree) { + PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } - while (numfreekeys) { - PyObject_FREE(keys_free_list[--numfreekeys]); + while (state->keys_numfree) { + PyObject_FREE(state->keys_free_list[--state->keys_numfree]); } -#endif } -/* Print summary info about the state of the optimized allocator */ + void -_PyDict_DebugMallocStats(FILE *out) +_PyDict_Fini(PyThreadState *tstate) { -#if PyDict_MAXFREELIST > 0 - _PyDebugAllocatorStats(out, - "free PyDictObject", numfree, sizeof(PyDictObject)); + _PyDict_ClearFreeList(tstate); +#ifdef Py_DEBUG + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; + state->numfree = -1; + state->keys_numfree = -1; #endif } +/* Print summary info about the state of the optimized allocator */ void -_PyDict_Fini(void) +_PyDict_DebugMallocStats(FILE *out) { - _PyDict_ClearFreeList(); + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; + _PyDebugAllocatorStats(out, "free PyDictObject", + state->numfree, sizeof(PyDictObject)); } + #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ @@ -543,7 +532,8 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) } -static PyDictKeysObject *new_keys_object(Py_ssize_t size) +static PyDictKeysObject* +new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; @@ -567,12 +557,16 @@ static PyDictKeysObject *new_keys_object(Py_ssize_t size) es = sizeof(Py_ssize_t); } -#if PyDict_MAXFREELIST > 0 - if (size == PyDict_MINSIZE && numfreekeys > 0) { - dk = keys_free_list[--numfreekeys]; + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; +#ifdef Py_DEBUG + // new_keys_object() must not be called after _PyDict_Fini() + assert(state->keys_numfree != -1); +#endif + if (size == PyDict_MINSIZE && state->keys_numfree > 0) { + dk = state->keys_free_list[--state->keys_numfree]; } else -#endif { dk = PyObject_MALLOC(sizeof(PyDictKeysObject) + es * size @@ -604,12 +598,16 @@ free_keys_object(PyDictKeysObject *keys) Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } -#if PyDict_MAXFREELIST > 0 - if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { - keys_free_list[numfreekeys++] = keys; + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; +#ifdef Py_DEBUG + // free_keys_object() must not be called after _PyDict_Fini() + assert(state->keys_numfree != -1); +#endif + if (keys->dk_size == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) { + state->keys_free_list[state->keys_numfree++] = keys; return; } -#endif PyObject_FREE(keys); } @@ -622,16 +620,19 @@ new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); -#if PyDict_MAXFREELIST > 0 - if (numfree) { - mp = free_list[--numfree]; + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; +#ifdef Py_DEBUG + // new_dict() must not be called after _PyDict_Fini() + assert(state->numfree != -1); +#endif + if (state->numfree) { + mp = state->free_list[--state->numfree]; assert (mp != NULL); assert (Py_IS_TYPE(mp, &PyDict_Type)); _Py_NewReference((PyObject *)mp); } - else -#endif - { + else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { dictkeys_decref(keys); @@ -1280,15 +1281,18 @@ dictresize(PyDictObject *mp, Py_ssize_t minsize) #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif -#if PyDict_MAXFREELIST > 0 + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; +#ifdef Py_DEBUG + // dictresize() must not be called after _PyDict_Fini() + assert(state->keys_numfree != -1); +#endif if (oldkeys->dk_size == PyDict_MINSIZE && - numfreekeys < PyDict_MAXFREELIST) + state->keys_numfree < PyDict_MAXFREELIST) { - keys_free_list[numfreekeys++] = oldkeys; + state->keys_free_list[state->keys_numfree++] = oldkeys; } - else -#endif - { + else { PyObject_FREE(oldkeys); } } @@ -2028,13 +2032,16 @@ dict_dealloc(PyDictObject *mp) assert(keys->dk_refcnt == 1); dictkeys_decref(keys); } -#if PyDict_MAXFREELIST > 0 - if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { - free_list[numfree++] = mp; - } - else + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_dict_state *state = &interp->dict_state; +#ifdef Py_DEBUG + // new_dict() must not be called after _PyDict_Fini() + assert(state->numfree != -1); #endif - { + if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { + state->free_list[state->numfree++] = mp; + } + else { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_END |