Implement compact dict

Issue #27350: `dict` implementation is changed like PyPy. It is more compact
and preserves insertion order.

_PyDict_Dummy() function has been removed.

Disable test_gdb: python-gdb.py is not updated yet to the new structure of
compact dictionaries (issue #28023).

Patch written by INADA Naoki.
This commit is contained in:
Victor Stinner 2016-09-07 17:40:12 -07:00
parent d8b7770a0e
commit 742da040db
12 changed files with 793 additions and 569 deletions

View file

@ -8,15 +8,25 @@ typedef struct {
PyObject *me_value; /* This field is only meaningful for combined tables */
} PyDictKeyEntry;
typedef PyDictKeyEntry *(*dict_lookup_func)
(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr);
/* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index].
* -1 when no entry found, -3 when compare raises error.
*/
typedef Py_ssize_t (*dict_lookup_func)
(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr,
Py_ssize_t *hashpos);
#define DKIX_EMPTY (-1)
#define DKIX_DUMMY (-2) /* Used internally */
#define DKIX_ERROR (-3)
/* See dictobject.c for actual layout of DictKeysObject */
struct _dictkeysobject {
Py_ssize_t dk_refcnt;
Py_ssize_t dk_size;
dict_lookup_func dk_lookup;
Py_ssize_t dk_usable;
PyDictKeyEntry dk_entries[1];
Py_ssize_t dk_nentries; /* How many entries are used. */
char dk_indices[8]; /* dynamically sized. 8 is minimum. */
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -22,12 +22,6 @@ _Py_GetRefTotal(void)
{
PyObject *o;
Py_ssize_t total = _Py_RefTotal;
/* ignore the references to the dummy object of the dicts and sets
because they are not reliable and not useful (now that the
hash table code is well-tested) */
o = _PyDict_Dummy();
if (o != NULL)
total -= o->ob_refcnt;
o = _PySet_Dummy;
if (o != NULL)
total -= o->ob_refcnt;

View file

@ -536,14 +536,17 @@ static Py_ssize_t
_odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
{
PyObject **value_addr = NULL;
PyDictKeyEntry *ep;
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
Py_ssize_t ix;
ep = (keys->dk_lookup)((PyDictObject *)od, key, hash, &value_addr);
if (ep == NULL)
ix = (keys->dk_lookup)((PyDictObject *)od, key, hash, &value_addr, NULL);
if (ix == DKIX_EMPTY) {
return keys->dk_nentries; /* index of new entry */
}
if (ix < 0)
return -1;
/* We use pointer arithmetic to get the entry's index into the table. */
return ep - keys->dk_entries;
return ix;
}
/* Replace od->od_fast_nodes with a new table matching the size of dict's. */
@ -565,7 +568,7 @@ _odict_resize(PyODictObject *od) {
/* Copy the current nodes into the table. */
_odict_FOREACH(od, node) {
i = _odict_get_index_raw(od, _odictnode_KEY(node),
_odictnode_HASH(node));
_odictnode_HASH(node));
if (i < 0) {
PyMem_FREE(fast_nodes);
return -1;