Issue #24483: C implementation of functools.lru_cache() now calculates key's

hash only once.
This commit is contained in:
Serhiy Storchaka 2015-10-02 12:47:11 +03:00
parent c9fda9b903
commit b9d98d532c
4 changed files with 64 additions and 6 deletions

View File

@ -72,6 +72,10 @@ PyAPI_FUNC(int) _PyDict_SetItem_KnownHash(PyObject *mp, PyObject *key,
PyObject *item, Py_hash_t hash); PyObject *item, Py_hash_t hash);
#endif #endif
PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key); PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key);
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyDict_DelItem_KnownHash(PyObject *mp, PyObject *key,
Py_hash_t hash);
#endif
PyAPI_FUNC(void) PyDict_Clear(PyObject *mp); PyAPI_FUNC(void) PyDict_Clear(PyObject *mp);
PyAPI_FUNC(int) PyDict_Next( PyAPI_FUNC(int) PyDict_Next(
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value); PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value);

View File

@ -32,6 +32,9 @@ Core and Builtins
Library Library
------- -------
- Issue #24483: C implementation of functools.lru_cache() now calculates key's
hash only once.
- Issue #22958: Constructor and update method of weakref.WeakValueDictionary - Issue #22958: Constructor and update method of weakref.WeakValueDictionary
now accept the self and the dict keyword arguments. now accept the self and the dict keyword arguments.

View File

@ -601,6 +601,7 @@ struct lru_cache_object;
typedef struct lru_list_elem { typedef struct lru_list_elem {
PyObject_HEAD PyObject_HEAD
struct lru_list_elem *prev, *next; /* borrowed links */ struct lru_list_elem *prev, *next; /* borrowed links */
Py_hash_t hash;
PyObject *key, *result; PyObject *key, *result;
} lru_list_elem; } lru_list_elem;
@ -762,10 +763,14 @@ static PyObject *
infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds) infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds)
{ {
PyObject *result; PyObject *result;
Py_hash_t hash;
PyObject *key = lru_cache_make_key(args, kwds, self->typed); PyObject *key = lru_cache_make_key(args, kwds, self->typed);
if (!key) if (!key)
return NULL; return NULL;
result = PyDict_GetItemWithError(self->cache, key); hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
result = _PyDict_GetItem_KnownHash(self->cache, key, hash);
if (result) { if (result) {
Py_INCREF(result); Py_INCREF(result);
self->hits++; self->hits++;
@ -781,7 +786,7 @@ infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwd
Py_DECREF(key); Py_DECREF(key);
return NULL; return NULL;
} }
if (PyDict_SetItem(self->cache, key, result) < 0) { if (_PyDict_SetItem_KnownHash(self->cache, key, result, hash) < 0) {
Py_DECREF(result); Py_DECREF(result);
Py_DECREF(key); Py_DECREF(key);
return NULL; return NULL;
@ -813,11 +818,15 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
{ {
lru_list_elem *link; lru_list_elem *link;
PyObject *key, *result; PyObject *key, *result;
Py_hash_t hash;
key = lru_cache_make_key(args, kwds, self->typed); key = lru_cache_make_key(args, kwds, self->typed);
if (!key) if (!key)
return NULL; return NULL;
link = (lru_list_elem *)PyDict_GetItemWithError(self->cache, key); hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
link = (lru_list_elem *)_PyDict_GetItem_KnownHash(self->cache, key, hash);
if (link) { if (link) {
lru_cache_extricate_link(link); lru_cache_extricate_link(link);
lru_cache_append_link(self, link); lru_cache_append_link(self, link);
@ -845,7 +854,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
/* Remove it from the cache. /* Remove it from the cache.
The cache dict holds one reference to the link, The cache dict holds one reference to the link,
and the linked list holds yet one reference to it. */ and the linked list holds yet one reference to it. */
if (PyDict_DelItem(self->cache, link->key) < 0) { if (_PyDict_DelItem_KnownHash(self->cache, link->key,
link->hash) < 0) {
lru_cache_append_link(self, link); lru_cache_append_link(self, link);
Py_DECREF(key); Py_DECREF(key);
Py_DECREF(result); Py_DECREF(result);
@ -859,9 +869,11 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
oldkey = link->key; oldkey = link->key;
oldresult = link->result; oldresult = link->result;
link->hash = hash;
link->key = key; link->key = key;
link->result = result; link->result = result;
if (PyDict_SetItem(self->cache, key, (PyObject *)link) < 0) { if (_PyDict_SetItem_KnownHash(self->cache, key, (PyObject *)link,
hash) < 0) {
Py_DECREF(link); Py_DECREF(link);
Py_DECREF(oldkey); Py_DECREF(oldkey);
Py_DECREF(oldresult); Py_DECREF(oldresult);
@ -881,10 +893,12 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
return NULL; return NULL;
} }
link->hash = hash;
link->key = key; link->key = key;
link->result = result; link->result = result;
_PyObject_GC_TRACK(link); _PyObject_GC_TRACK(link);
if (PyDict_SetItem(self->cache, key, (PyObject *)link) < 0) { if (_PyDict_SetItem_KnownHash(self->cache, key, (PyObject *)link,
hash) < 0) {
Py_DECREF(link); Py_DECREF(link);
return NULL; return NULL;
} }

View File

@ -1242,6 +1242,7 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
} }
assert(key); assert(key);
assert(value); assert(value);
assert(hash != -1);
mp = (PyDictObject *)op; mp = (PyDictObject *)op;
/* insertdict() handles any resizing that might be necessary */ /* insertdict() handles any resizing that might be necessary */
@ -1290,6 +1291,42 @@ PyDict_DelItem(PyObject *op, PyObject *key)
return 0; return 0;
} }
int
_PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
{
PyDictObject *mp;
PyDictKeyEntry *ep;
PyObject *old_key, *old_value;
PyObject **value_addr;
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
assert(hash != -1);
mp = (PyDictObject *)op;
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
if (ep == NULL)
return -1;
if (*value_addr == NULL) {
_PyErr_SetKeyError(key);
return -1;
}
old_value = *value_addr;
*value_addr = NULL;
mp->ma_used--;
if (!_PyDict_HasSplitTable(mp)) {
ENSURE_ALLOWS_DELETIONS(mp);
old_key = ep->me_key;
Py_INCREF(dummy);
ep->me_key = dummy;
Py_DECREF(old_key);
}
Py_DECREF(old_value);
return 0;
}
void void
PyDict_Clear(PyObject *op) PyDict_Clear(PyObject *op)
{ {