Commit 49f5cdde authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #28183: Optimize and cleanup dict iteration.

parent 06060725
...@@ -10,6 +10,8 @@ What's New in Python 3.6.0 beta 2 ...@@ -10,6 +10,8 @@ What's New in Python 3.6.0 beta 2
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #28183: Optimize and cleanup dict iteration.
- Issue #26081: Added C implementation of asyncio.Future. - Issue #26081: Added C implementation of asyncio.Future.
Original patch by Yury Selivanov. Original patch by Yury Selivanov.
......
...@@ -1690,43 +1690,56 @@ PyDict_Clear(PyObject *op) ...@@ -1690,43 +1690,56 @@ PyDict_Clear(PyObject *op)
assert(_PyDict_CheckConsistency(mp)); assert(_PyDict_CheckConsistency(mp));
} }
/* Returns -1 if no more items (or op is not a dict), /* Internal version of PyDict_Next that returns a hash value in addition
* index of item otherwise. Stores value in pvalue * to the key and value.
* Return 1 on success, return 0 when the reached the end of the dictionary
* (or if op is not a dictionary)
*/ */
static inline Py_ssize_t int
dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue) _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
PyObject **pvalue, Py_hash_t *phash)
{ {
Py_ssize_t n; Py_ssize_t i, n;
PyDictObject *mp; PyDictObject *mp;
PyObject **value_ptr = NULL; PyDictKeyEntry *entry_ptr;
PyObject *value;
if (!PyDict_Check(op)) if (!PyDict_Check(op))
return -1; return 0;
mp = (PyDictObject *)op; mp = (PyDictObject *)op;
if (i < 0) i = *ppos;
return -1;
n = mp->ma_keys->dk_nentries; n = mp->ma_keys->dk_nentries;
if ((size_t)i >= (size_t)n)
return 0;
if (mp->ma_values) { if (mp->ma_values) {
for (; i < n; i++) { PyObject **value_ptr = &mp->ma_values[i];
value_ptr = &mp->ma_values[i]; while (i < n && *value_ptr == NULL) {
if (*value_ptr != NULL) value_ptr++;
break; i++;
} }
if (i >= n)
return 0;
entry_ptr = &DK_ENTRIES(mp->ma_keys)[i];
value = *value_ptr;
} }
else { else {
PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); entry_ptr = &DK_ENTRIES(mp->ma_keys)[i];
for (; i < n; i++) { while (i < n && entry_ptr->me_value == NULL) {
value_ptr = &ep0[i].me_value; entry_ptr++;
if (*value_ptr != NULL) i++;
break;
} }
if (i >= n)
return 0;
value = entry_ptr->me_value;
} }
if (i >= n) *ppos = i+1;
return -1; if (pkey)
*pkey = entry_ptr->me_key;
if (phash)
*phash = entry_ptr->me_hash;
if (pvalue) if (pvalue)
*pvalue = *value_ptr; *pvalue = value;
return i; return 1;
} }
/* /*
...@@ -1736,9 +1749,12 @@ dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue) ...@@ -1736,9 +1749,12 @@ dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue)
* PyObject *key, *value; * PyObject *key, *value;
* i = 0; # important! i should not otherwise be changed by you * i = 0; # important! i should not otherwise be changed by you
* while (PyDict_Next(yourdict, &i, &key, &value)) { * while (PyDict_Next(yourdict, &i, &key, &value)) {
* Refer to borrowed references in key and value. * Refer to borrowed references in key and value.
* } * }
* *
* Return 1 on success, return 0 when the reached the end of the dictionary
* (or if op is not a dictionary)
*
* CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that
* mutates the dict. One exception: it is safe if the loop merely changes * mutates the dict. One exception: it is safe if the loop merely changes
* the values associated with the keys (but doesn't insert new keys or * the values associated with the keys (but doesn't insert new keys or
...@@ -1747,36 +1763,7 @@ dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue) ...@@ -1747,36 +1763,7 @@ dict_next(PyObject *op, Py_ssize_t i, PyObject **pvalue)
int int
PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
{ {
PyDictObject *mp = (PyDictObject*)op; return _PyDict_Next(op, ppos, pkey, pvalue, NULL);
Py_ssize_t i = dict_next(op, *ppos, pvalue);
if (i < 0)
return 0;
mp = (PyDictObject *)op;
*ppos = i+1;
if (pkey)
*pkey = DK_ENTRIES(mp->ma_keys)[i].me_key;
return 1;
}
/* Internal version of PyDict_Next that returns a hash value in addition
* to the key and value.
*/
int
_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
PyObject **pvalue, Py_hash_t *phash)
{
PyDictObject *mp;
PyDictKeyEntry *ep0;
Py_ssize_t i = dict_next(op, *ppos, pvalue);
if (i < 0)
return 0;
mp = (PyDictObject *)op;
ep0 = DK_ENTRIES(mp->ma_keys);
*ppos = i+1;
*phash = ep0[i].me_hash;
if (pkey)
*pkey = ep0[i].me_key;
return 1;
} }
/* Internal version of dict.pop(). */ /* Internal version of dict.pop(). */
...@@ -3352,13 +3339,13 @@ static PyMethodDef dictiter_methods[] = { ...@@ -3352,13 +3339,13 @@ static PyMethodDef dictiter_methods[] = {
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
static PyObject *dictiter_iternextkey(dictiterobject *di) static PyObject*
dictiter_iternextkey(dictiterobject *di)
{ {
PyObject *key; PyObject *key;
Py_ssize_t i, n, offset; Py_ssize_t i, n;
PyDictKeysObject *k; PyDictKeysObject *k;
PyDictObject *d = di->di_dict; PyDictObject *d = di->di_dict;
PyObject **value_ptr;
if (d == NULL) if (d == NULL)
return NULL; return NULL;
...@@ -3372,27 +3359,30 @@ static PyObject *dictiter_iternextkey(dictiterobject *di) ...@@ -3372,27 +3359,30 @@ static PyObject *dictiter_iternextkey(dictiterobject *di)
} }
i = di->di_pos; i = di->di_pos;
if (i < 0)
goto fail;
k = d->ma_keys; k = d->ma_keys;
n = k->dk_nentries;
if (d->ma_values) { if (d->ma_values) {
value_ptr = &d->ma_values[i]; PyObject **value_ptr = &d->ma_values[i];
offset = sizeof(PyObject *); while (i < n && *value_ptr == NULL) {
value_ptr++;
i++;
}
if (i >= n)
goto fail;
key = DK_ENTRIES(k)[i].me_key;
} }
else { else {
value_ptr = &DK_ENTRIES(k)[i].me_value; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i];
offset = sizeof(PyDictKeyEntry); while (i < n && entry_ptr->me_value == NULL) {
} entry_ptr++;
n = k->dk_nentries - 1; i++;
while (i <= n && *value_ptr == NULL) { }
value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (i >= n)
i++; goto fail;
key = entry_ptr->me_key;
} }
di->di_pos = i+1; di->di_pos = i+1;
if (i > n)
goto fail;
di->len--; di->len--;
key = DK_ENTRIES(k)[i].me_key;
Py_INCREF(key); Py_INCREF(key);
return key; return key;
...@@ -3435,12 +3425,12 @@ PyTypeObject PyDictIterKey_Type = { ...@@ -3435,12 +3425,12 @@ PyTypeObject PyDictIterKey_Type = {
0, 0,
}; };
static PyObject *dictiter_iternextvalue(dictiterobject *di) static PyObject *
dictiter_iternextvalue(dictiterobject *di)
{ {
PyObject *value; PyObject *value;
Py_ssize_t i, n, offset; Py_ssize_t i, n;
PyDictObject *d = di->di_dict; PyDictObject *d = di->di_dict;
PyObject **value_ptr;
if (d == NULL) if (d == NULL)
return NULL; return NULL;
...@@ -3454,26 +3444,29 @@ static PyObject *dictiter_iternextvalue(dictiterobject *di) ...@@ -3454,26 +3444,29 @@ static PyObject *dictiter_iternextvalue(dictiterobject *di)
} }
i = di->di_pos; i = di->di_pos;
n = d->ma_keys->dk_nentries - 1; n = d->ma_keys->dk_nentries;
if (i < 0 || i > n)
goto fail;
if (d->ma_values) { if (d->ma_values) {
value_ptr = &d->ma_values[i]; PyObject **value_ptr = &d->ma_values[i];
offset = sizeof(PyObject *); while (i < n && *value_ptr == NULL) {
value_ptr++;
i++;
}
if (i >= n)
goto fail;
value = *value_ptr;
} }
else { else {
value_ptr = &DK_ENTRIES(d->ma_keys)[i].me_value; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i];
offset = sizeof(PyDictKeyEntry); while (i < n && entry_ptr->me_value == NULL) {
} entry_ptr++;
while (i <= n && *value_ptr == NULL) { i++;
value_ptr = (PyObject **)(((char *)value_ptr) + offset); }
i++; if (i >= n)
if (i > n)
goto fail; goto fail;
value = entry_ptr->me_value;
} }
di->di_pos = i+1; di->di_pos = i+1;
di->len--; di->len--;
value = *value_ptr;
Py_INCREF(value); Py_INCREF(value);
return value; return value;
...@@ -3504,7 +3497,7 @@ PyTypeObject PyDictIterValue_Type = { ...@@ -3504,7 +3497,7 @@ PyTypeObject PyDictIterValue_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
(traverseproc)dictiter_traverse, /* tp_traverse */ (traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
...@@ -3516,12 +3509,12 @@ PyTypeObject PyDictIterValue_Type = { ...@@ -3516,12 +3509,12 @@ PyTypeObject PyDictIterValue_Type = {
0, 0,
}; };
static PyObject *dictiter_iternextitem(dictiterobject *di) static PyObject *
dictiter_iternextitem(dictiterobject *di)
{ {
PyObject *key, *value, *result = di->di_result; PyObject *key, *value, *result = di->di_result;
Py_ssize_t i, n, offset; Py_ssize_t i, n;
PyDictObject *d = di->di_dict; PyDictObject *d = di->di_dict;
PyObject **value_ptr;
if (d == NULL) if (d == NULL)
return NULL; return NULL;
...@@ -3535,37 +3528,41 @@ static PyObject *dictiter_iternextitem(dictiterobject *di) ...@@ -3535,37 +3528,41 @@ static PyObject *dictiter_iternextitem(dictiterobject *di)
} }
i = di->di_pos; i = di->di_pos;
if (i < 0) n = d->ma_keys->dk_nentries;
goto fail;
n = d->ma_keys->dk_nentries - 1;
if (d->ma_values) { if (d->ma_values) {
value_ptr = &d->ma_values[i]; PyObject **value_ptr = &d->ma_values[i];
offset = sizeof(PyObject *); while (i < n && *value_ptr == NULL) {
value_ptr++;
i++;
}
if (i >= n)
goto fail;
key = DK_ENTRIES(d->ma_keys)[i].me_key;
value = *value_ptr;
} }
else { else {
value_ptr = &DK_ENTRIES(d->ma_keys)[i].me_value; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i];
offset = sizeof(PyDictKeyEntry); while (i < n && entry_ptr->me_value == NULL) {
} entry_ptr++;
while (i <= n && *value_ptr == NULL) { i++;
value_ptr = (PyObject **)(((char *)value_ptr) + offset); }
i++; if (i >= n)
goto fail;
key = entry_ptr->me_key;
value = entry_ptr->me_value;
} }
di->di_pos = i+1; di->di_pos = i+1;
if (i > n) di->len--;
goto fail;
if (result->ob_refcnt == 1) { if (result->ob_refcnt == 1) {
Py_INCREF(result); Py_INCREF(result);
Py_DECREF(PyTuple_GET_ITEM(result, 0)); Py_DECREF(PyTuple_GET_ITEM(result, 0));
Py_DECREF(PyTuple_GET_ITEM(result, 1)); Py_DECREF(PyTuple_GET_ITEM(result, 1));
} else { }
else {
result = PyTuple_New(2); result = PyTuple_New(2);
if (result == NULL) if (result == NULL)
return NULL; return NULL;
} }
di->len--;
key = DK_ENTRIES(d->ma_keys)[i].me_key;
value = *value_ptr;
Py_INCREF(key); Py_INCREF(key);
Py_INCREF(value); Py_INCREF(value);
PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 0, key); /* steals reference */
......
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