diff options
author | Raymond Hettinger <python@rcn.com> | 2004-03-17 21:55:03 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2004-03-17 21:55:03 (GMT) |
commit | 4344278250a2994aefc1be03dffc37e1c35e4992 (patch) | |
tree | bd6fd21b51da765ae7588dca958d11819aa77c6d /Objects | |
parent | 969d8c0c8cbef8f8b838c3032bb6beb60cb59f4f (diff) | |
download | cpython-4344278250a2994aefc1be03dffc37e1c35e4992.zip cpython-4344278250a2994aefc1be03dffc37e1c35e4992.tar.gz cpython-4344278250a2994aefc1be03dffc37e1c35e4992.tar.bz2 |
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.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/dictobject.c | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0667bdc..1aba5a2 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -152,6 +152,11 @@ show_counts(void) INIT_NONZERO_DICT_SLOTS(mp); \ } 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 * PyDict_New(void) { @@ -164,10 +169,23 @@ PyDict_New(void) Py_AtExit(show_counts); #endif } - mp = PyObject_GC_New(dictobject, &PyDict_Type); - if (mp == NULL) - return NULL; - EMPTY_TO_MINSIZE(mp); + 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); + if (mp == NULL) + return NULL; + EMPTY_TO_MINSIZE(mp); + } mp->ma_lookup = lookdict_string; #ifdef SHOW_CONVERSION_COUNTS ++created; @@ -672,23 +690,25 @@ PyDict_Clear(PyObject *op) int PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue) { - int i; - register dictobject *mp; + register int i, mask; + register dictentry *ep; + if (!PyDict_Check(op)) return 0; - mp = (dictobject *)op; i = *ppos; if (i < 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++; *ppos = i+1; - if (i > mp->ma_mask) + if (i > mask) return 0; if (pkey) - *pkey = mp->ma_table[i].me_key; + *pkey = ep[i].me_key; if (pvalue) - *pvalue = mp->ma_table[i].me_value; + *pvalue = ep[i].me_value; return 1; } @@ -710,7 +730,10 @@ dict_dealloc(register dictobject *mp) } if (mp->ma_table != mp->ma_smalltable) PyMem_DEL(mp->ma_table); - mp->ob_type->tp_free((PyObject *)mp); + if (num_free_dicts < MAXFREEDICTS && mp->ob_type == &PyDict_Type) + free_dicts[num_free_dicts++] = mp; + else + mp->ob_type->tp_free((PyObject *)mp); Py_TRASHCAN_SAFE_END(mp) } @@ -882,7 +905,9 @@ static PyObject * dict_keys(register dictobject *mp) { register PyObject *v; - register int i, j, n; + register int i, j; + dictentry *ep; + int mask, n; again: n = mp->ma_used; @@ -896,14 +921,17 @@ dict_keys(register dictobject *mp) Py_DECREF(v); goto again; } - for (i = 0, j = 0; i <= mp->ma_mask; i++) { - if (mp->ma_table[i].me_value != NULL) { - PyObject *key = mp->ma_table[i].me_key; + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if (ep[i].me_value != NULL) { + PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } } + assert(j == n); return v; } @@ -911,7 +939,9 @@ static PyObject * dict_values(register dictobject *mp) { register PyObject *v; - register int i, j, n; + register int i, j; + dictentry *ep; + int mask, n; again: n = mp->ma_used; @@ -925,14 +955,17 @@ dict_values(register dictobject *mp) Py_DECREF(v); goto again; } - for (i = 0, j = 0; i <= mp->ma_mask; i++) { - if (mp->ma_table[i].me_value != NULL) { - PyObject *value = mp->ma_table[i].me_value; + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if (ep[i].me_value != NULL) { + PyObject *value = ep[i].me_value; Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } + assert(j == n); return v; } @@ -941,7 +974,9 @@ dict_items(register dictobject *mp) { register PyObject *v; register int i, j, n; + int mask; PyObject *item, *key, *value; + dictentry *ep; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which @@ -968,10 +1003,12 @@ dict_items(register dictobject *mp) goto again; } /* Nothing we do below makes any function calls. */ - for (i = 0, j = 0; i <= mp->ma_mask; i++) { - if (mp->ma_table[i].me_value != NULL) { - key = mp->ma_table[i].me_key; - value = mp->ma_table[i].me_value; + ep = mp->ma_table; + mask = mp->ma_mask; + for (i = 0, j = 0; i <= mask; i++) { + if (ep[i].me_value != NULL) { + key = ep[i].me_key; + value = ep[i].me_value; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); |