Browse Source

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

hash only once.
pull/9921/head
Serhiy Storchaka 10 years ago
parent
commit
b9d98d532c
  1. 4
      Include/dictobject.h
  2. 3
      Misc/NEWS
  3. 26
      Modules/_functoolsmodule.c
  4. 37
      Objects/dictobject.c

4
Include/dictobject.h

@ -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);

3
Misc/NEWS

@ -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.

26
Modules/_functoolsmodule.c

@ -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;
} }

37
Objects/dictobject.c

@ -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)
{ {

Loading…
Cancel
Save