Commit 43442782 authored by Raymond Hettinger's avatar Raymond Hettinger

Dictionary optimizations:

* Factored constant structure references out of the inner loops for
  PyDict_Next(), dict_keys(), dict_values(), and dict_items().
  Gave measurable speedups to each (the improvement varies depending
  on the sparseness of the dictionary being measured).

* Added a freelist scheme styled after that for tuples.  Saves around
  80% of the calls to malloc and free.  About 10% of the time, the
  previous dictionary was completely empty; in those cases, the
  dictionary initialization with memset() can be skipped.
parent 969d8c0c
...@@ -152,6 +152,11 @@ show_counts(void) ...@@ -152,6 +152,11 @@ show_counts(void)
INIT_NONZERO_DICT_SLOTS(mp); \ INIT_NONZERO_DICT_SLOTS(mp); \
} while(0) } while(0)
/* Dictionary reuse scheme to save calls to malloc, free, and memset */
#define MAXFREEDICTS 80
static PyDictObject *free_dicts[MAXFREEDICTS];
static int num_free_dicts = 0;
PyObject * PyObject *
PyDict_New(void) PyDict_New(void)
{ {
...@@ -164,10 +169,23 @@ PyDict_New(void) ...@@ -164,10 +169,23 @@ PyDict_New(void)
Py_AtExit(show_counts); Py_AtExit(show_counts);
#endif #endif
} }
if (num_free_dicts) {
mp = free_dicts[--num_free_dicts];
assert (mp != NULL);
assert (mp->ob_type == &PyDict_Type);
_Py_NewReference((PyObject *)mp);
if (mp->ma_fill) {
EMPTY_TO_MINSIZE(mp);
}
assert (mp->ma_used == 0);
assert (mp->ma_table == mp->ma_smalltable);
assert (mp->ma_mask == PyDict_MINSIZE - 1);
} else {
mp = PyObject_GC_New(dictobject, &PyDict_Type); mp = PyObject_GC_New(dictobject, &PyDict_Type);
if (mp == NULL) if (mp == NULL)
return NULL; return NULL;
EMPTY_TO_MINSIZE(mp); EMPTY_TO_MINSIZE(mp);
}
mp->ma_lookup = lookdict_string; mp->ma_lookup = lookdict_string;
#ifdef SHOW_CONVERSION_COUNTS #ifdef SHOW_CONVERSION_COUNTS
++created; ++created;
...@@ -672,23 +690,25 @@ PyDict_Clear(PyObject *op) ...@@ -672,23 +690,25 @@ PyDict_Clear(PyObject *op)
int int
PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue) PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue)
{ {
int i; register int i, mask;
register dictobject *mp; register dictentry *ep;
if (!PyDict_Check(op)) if (!PyDict_Check(op))
return 0; return 0;
mp = (dictobject *)op;
i = *ppos; i = *ppos;
if (i < 0) if (i < 0)
return 0; return 0;
while (i <= mp->ma_mask && mp->ma_table[i].me_value == NULL) ep = ((dictobject *)op)->ma_table;
mask = ((dictobject *)op)->ma_mask;
while (i <= mask && ep[i].me_value == NULL)
i++; i++;
*ppos = i+1; *ppos = i+1;
if (i > mp->ma_mask) if (i > mask)
return 0; return 0;
if (pkey) if (pkey)
*pkey = mp->ma_table[i].me_key; *pkey = ep[i].me_key;
if (pvalue) if (pvalue)
*pvalue = mp->ma_table[i].me_value; *pvalue = ep[i].me_value;
return 1; return 1;
} }
...@@ -710,6 +730,9 @@ dict_dealloc(register dictobject *mp) ...@@ -710,6 +730,9 @@ dict_dealloc(register dictobject *mp)
} }
if (mp->ma_table != mp->ma_smalltable) if (mp->ma_table != mp->ma_smalltable)
PyMem_DEL(mp->ma_table); PyMem_DEL(mp->ma_table);
if (num_free_dicts < MAXFREEDICTS && mp->ob_type == &PyDict_Type)
free_dicts[num_free_dicts++] = mp;
else
mp->ob_type->tp_free((PyObject *)mp); mp->ob_type->tp_free((PyObject *)mp);
Py_TRASHCAN_SAFE_END(mp) Py_TRASHCAN_SAFE_END(mp)
} }
...@@ -882,7 +905,9 @@ static PyObject * ...@@ -882,7 +905,9 @@ static PyObject *
dict_keys(register dictobject *mp) dict_keys(register dictobject *mp)
{ {
register PyObject *v; register PyObject *v;
register int i, j, n; register int i, j;
dictentry *ep;
int mask, n;
again: again:
n = mp->ma_used; n = mp->ma_used;
...@@ -896,14 +921,17 @@ dict_keys(register dictobject *mp) ...@@ -896,14 +921,17 @@ dict_keys(register dictobject *mp)
Py_DECREF(v); Py_DECREF(v);
goto again; goto again;
} }
for (i = 0, j = 0; i <= mp->ma_mask; i++) { ep = mp->ma_table;
if (mp->ma_table[i].me_value != NULL) { mask = mp->ma_mask;
PyObject *key = mp->ma_table[i].me_key; for (i = 0, j = 0; i <= mask; i++) {
if (ep[i].me_value != NULL) {
PyObject *key = ep[i].me_key;
Py_INCREF(key); Py_INCREF(key);
PyList_SET_ITEM(v, j, key); PyList_SET_ITEM(v, j, key);
j++; j++;
} }
} }
assert(j == n);
return v; return v;
} }
...@@ -911,7 +939,9 @@ static PyObject * ...@@ -911,7 +939,9 @@ static PyObject *
dict_values(register dictobject *mp) dict_values(register dictobject *mp)
{ {
register PyObject *v; register PyObject *v;
register int i, j, n; register int i, j;
dictentry *ep;
int mask, n;
again: again:
n = mp->ma_used; n = mp->ma_used;
...@@ -925,14 +955,17 @@ dict_values(register dictobject *mp) ...@@ -925,14 +955,17 @@ dict_values(register dictobject *mp)
Py_DECREF(v); Py_DECREF(v);
goto again; goto again;
} }
for (i = 0, j = 0; i <= mp->ma_mask; i++) { ep = mp->ma_table;
if (mp->ma_table[i].me_value != NULL) { mask = mp->ma_mask;
PyObject *value = mp->ma_table[i].me_value; for (i = 0, j = 0; i <= mask; i++) {
if (ep[i].me_value != NULL) {
PyObject *value = ep[i].me_value;
Py_INCREF(value); Py_INCREF(value);
PyList_SET_ITEM(v, j, value); PyList_SET_ITEM(v, j, value);
j++; j++;
} }
} }
assert(j == n);
return v; return v;
} }
...@@ -941,7 +974,9 @@ dict_items(register dictobject *mp) ...@@ -941,7 +974,9 @@ dict_items(register dictobject *mp)
{ {
register PyObject *v; register PyObject *v;
register int i, j, n; register int i, j, n;
int mask;
PyObject *item, *key, *value; PyObject *item, *key, *value;
dictentry *ep;
/* Preallocate the list of tuples, to avoid allocations during /* Preallocate the list of tuples, to avoid allocations during
* the loop over the items, which could trigger GC, which * the loop over the items, which could trigger GC, which
...@@ -968,10 +1003,12 @@ dict_items(register dictobject *mp) ...@@ -968,10 +1003,12 @@ dict_items(register dictobject *mp)
goto again; goto again;
} }
/* Nothing we do below makes any function calls. */ /* Nothing we do below makes any function calls. */
for (i = 0, j = 0; i <= mp->ma_mask; i++) { ep = mp->ma_table;
if (mp->ma_table[i].me_value != NULL) { mask = mp->ma_mask;
key = mp->ma_table[i].me_key; for (i = 0, j = 0; i <= mask; i++) {
value = mp->ma_table[i].me_value; if (ep[i].me_value != NULL) {
key = ep[i].me_key;
value = ep[i].me_value;
item = PyList_GET_ITEM(v, j); item = PyList_GET_ITEM(v, j);
Py_INCREF(key); Py_INCREF(key);
PyTuple_SET_ITEM(item, 0, key); PyTuple_SET_ITEM(item, 0, key);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment