From 4fd78e4c0c7705aeb7859745dbd34747200a923c Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Apr 2021 18:28:49 -0700 Subject: [PATCH 1/5] bpo-40137: Move state lookups out of the critical path --- Modules/_functoolsmodule.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 1fcaf299e67bc6..c6b2d6046b13a5 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -781,13 +781,15 @@ typedef struct lru_cache_object { PyObject *func; Py_ssize_t maxsize; Py_ssize_t misses; + PyObject *kwd_mark; + PyTypeObject *lru_list_elem_type; PyObject *cache_info_type; PyObject *dict; PyObject *weakreflist; } lru_cache_object; static PyObject * -lru_cache_make_key(_functools_state *state, PyObject *args, +lru_cache_make_key(PyObject *kwd_mark, PyObject *args, PyObject *kwds, int typed) { PyObject *key, *keyword, *value; @@ -827,8 +829,8 @@ lru_cache_make_key(_functools_state *state, PyObject *args, PyTuple_SET_ITEM(key, key_pos++, item); } if (kwds_size) { - Py_INCREF(state->kwd_mark); - PyTuple_SET_ITEM(key, key_pos++, state->kwd_mark); + Py_INCREF(kwd_mark); + PyTuple_SET_ITEM(key, key_pos++, kwd_mark); for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) { Py_INCREF(keyword); PyTuple_SET_ITEM(key, key_pos++, keyword); @@ -871,13 +873,10 @@ static PyObject * infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds) { PyObject *result; + PyObject *key; Py_hash_t hash; - _functools_state *state; - state = get_functools_state_by_type(Py_TYPE(self)); - if (state == NULL) { - return NULL; - } - PyObject *key = lru_cache_make_key(state, args, kwds, self->typed); + + key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed); if (!key) return NULL; hash = PyObject_Hash(key); @@ -977,13 +976,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds lru_list_elem *link; PyObject *key, *result, *testresult; Py_hash_t hash; - _functools_state *state; - state = get_functools_state_by_type(Py_TYPE(self)); - if (state == NULL) { - return NULL; - } - key = lru_cache_make_key(state, args, kwds, self->typed); + key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed); if (!key) return NULL; hash = PyObject_Hash(key); @@ -1038,7 +1032,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds { /* Cache is not full, so put the result in a new link */ link = (lru_list_elem *)PyObject_New(lru_list_elem, - state->lru_list_elem_type); + self->lru_list_elem_type); if (link == NULL) { Py_DECREF(key); Py_DECREF(result); @@ -1149,6 +1143,7 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) lru_cache_object *obj; Py_ssize_t maxsize; PyObject *(*wrapper)(lru_cache_object *, PyObject *, PyObject *); + _functools_state *state; static char *keywords[] = {"user_function", "maxsize", "typed", "cache_info_type", NULL}; @@ -1194,6 +1189,13 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } + state = get_functools_state_by_type(Py_TYPE(obj)); + if (state == NULL) { + Py_DECREF(cachedict); + Py_DECREF(obj); + return NULL; + } + obj->root.prev = &obj->root; obj->root.next = &obj->root; obj->wrapper = wrapper; @@ -1203,6 +1205,8 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) obj->func = func; obj->misses = obj->hits = 0; obj->maxsize = maxsize; + obj->kwd_mark = state->kwd_mark; // Borrowed + obj->lru_list_elem_type = state->lru_list_elem_type; // Borrowed Py_INCREF(cache_info_type); obj->cache_info_type = cache_info_type; obj->dict = NULL; From 785eb24365926a383a27ac6bcfa82ced4a045a1b Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Apr 2021 18:38:24 -0700 Subject: [PATCH 2/5] Repair file organization --- Modules/_functoolsmodule.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index c6b2d6046b13a5..9d1cf92ec24581 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -12,18 +12,6 @@ All rights reserved. */ -/* partial object **********************************************************/ - -typedef struct { - PyObject_HEAD - PyObject *fn; - PyObject *args; - PyObject *kw; - PyObject *dict; /* __dict__ */ - PyObject *weakreflist; /* List of weak references */ - vectorcallfunc vectorcall; -} partialobject; - typedef struct _functools_state { /* this object is used delimit args and keywords in the cache keys */ PyObject *kwd_mark; @@ -40,6 +28,19 @@ get_functools_state(PyObject *module) return (_functools_state *)state; } + +/* partial object **********************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *fn; + PyObject *args; + PyObject *kw; + PyObject *dict; /* __dict__ */ + PyObject *weakreflist; /* List of weak references */ + vectorcallfunc vectorcall; +} partialobject; + static void partial_setvectorcall(partialobject *pto); static struct PyModuleDef _functools_module; static PyObject * From 58bc078bc9bb46a4df570a6d3fe44983b84dde8c Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Apr 2021 19:06:41 -0700 Subject: [PATCH 3/5] Code clean-ups to match 3.9 as closely as possible --- Modules/_functoolsmodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 9d1cf92ec24581..56e233920cfcb6 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -782,6 +782,7 @@ typedef struct lru_cache_object { PyObject *func; Py_ssize_t maxsize; Py_ssize_t misses; + /* the kwd_mark is used delimit args and keywords in the cache keys */ PyObject *kwd_mark; PyTypeObject *lru_list_elem_type; PyObject *cache_info_type; @@ -874,10 +875,8 @@ static PyObject * infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds) { PyObject *result; - PyObject *key; Py_hash_t hash; - - key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed); + PyObject *key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed); if (!key) return NULL; hash = PyObject_Hash(key); From a1e622e46c50c0f0220314f6e2dbf56beddade2c Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 21 Apr 2021 08:13:21 -0700 Subject: [PATCH 4/5] Use hard references --- Modules/_functoolsmodule.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 56e233920cfcb6..9eefad01c254c1 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1205,8 +1205,10 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) obj->func = func; obj->misses = obj->hits = 0; obj->maxsize = maxsize; - obj->kwd_mark = state->kwd_mark; // Borrowed - obj->lru_list_elem_type = state->lru_list_elem_type; // Borrowed + Py_INCREF(state->kwd_mark); + obj->kwd_mark = state->kwd_mark; + Py_INCREF(state->lru_list_elem_type); + obj->lru_list_elem_type = state->lru_list_elem_type; Py_INCREF(cache_info_type); obj->cache_info_type = cache_info_type; obj->dict = NULL; @@ -1242,6 +1244,8 @@ lru_cache_tp_clear(lru_cache_object *self) lru_list_elem *list = lru_cache_unlink_list(self); Py_CLEAR(self->func); Py_CLEAR(self->cache); + Py_CLEAR(self->kwd_mark); + Py_CLEAR(self->lru_list_elem_type); Py_CLEAR(self->cache_info_type); Py_CLEAR(self->dict); lru_cache_clear_list(list); @@ -1334,6 +1338,8 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) } Py_VISIT(self->func); Py_VISIT(self->cache); + Py_VISIT(self->kwd_mark); + Py_VISIT(self->lru_list_elem_type); Py_VISIT(self->cache_info_type); Py_VISIT(self->dict); return 0; From 5a03e52d87b46d39d889aa67bff331c5e8d1e8a0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 21 Apr 2021 11:30:39 -0700 Subject: [PATCH 5/5] Avoid decrefs by moving state lookup earlier in the function --- Modules/_functoolsmodule.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 9eefad01c254c1..f3ae3b62ae332c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1159,6 +1159,11 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } + state = get_functools_state_by_type(type); + if (state == NULL) { + return NULL; + } + /* select the caching function, and make/inc maxsize_O */ if (maxsize_O == Py_None) { wrapper = infinite_lru_cache_wrapper; @@ -1189,13 +1194,6 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } - state = get_functools_state_by_type(Py_TYPE(obj)); - if (state == NULL) { - Py_DECREF(cachedict); - Py_DECREF(obj); - return NULL; - } - obj->root.prev = &obj->root; obj->root.next = &obj->root; obj->wrapper = wrapper;