From b684ce110a637990690b650e3201bbdfef8c6194 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Mon, 28 Oct 2019 20:56:10 +0900 Subject: [PATCH 1/5] bpo-38613: Optimize set operations of dict keys. --- Objects/dictobject.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index d909f220a984b1b..1f60c7e0375b57e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4165,14 +4165,17 @@ static PySequenceMethods dictkeys_as_sequence = { static PyObject* dictviews_sub(PyObject* self, PyObject *other) { + if (PyDictKeys_Check(self)) { + self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + } PyObject *result = PySet_New(self); - PyObject *tmp; - _Py_IDENTIFIER(difference_update); - - if (result == NULL) + if (result == NULL) { return NULL; + } - tmp = _PyObject_CallMethodIdOneArg(result, &PyId_difference_update, other); + _Py_IDENTIFIER(difference_update); + PyObject *tmp = _PyObject_CallMethodIdOneArg( + result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4273,14 +4276,17 @@ _PyDictView_Intersect(PyObject* self, PyObject *other) static PyObject* dictviews_or(PyObject* self, PyObject *other) { + if (PyDictKeys_Check(self)) { + self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + } PyObject *result = PySet_New(self); - PyObject *tmp; - _Py_IDENTIFIER(update); - - if (result == NULL) + if (result == NULL) { return NULL; + } - tmp = _PyObject_CallMethodIdOneArg(result, &PyId_update, other); + _Py_IDENTIFIER(update); + PyObject *tmp = _PyObject_CallMethodIdOneArg( + result, &PyId_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4293,14 +4299,17 @@ dictviews_or(PyObject* self, PyObject *other) static PyObject* dictviews_xor(PyObject* self, PyObject *other) { + if (PyDictKeys_Check(self)) { + self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + } PyObject *result = PySet_New(self); - PyObject *tmp; - _Py_IDENTIFIER(symmetric_difference_update); - - if (result == NULL) + if (result == NULL) { return NULL; + } - tmp = _PyObject_CallMethodIdOneArg(result, &PyId_symmetric_difference_update, other); + _Py_IDENTIFIER(symmetric_difference_update); + PyObject *tmp = _PyObject_CallMethodIdOneArg( + result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; From ed139f4278a51d5c6de05f2d6316a4c2d6a2e42b Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Mon, 28 Oct 2019 21:32:29 +0900 Subject: [PATCH 2/5] code cleanup --- Objects/dictobject.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 1f60c7e0375b57e..d54b6c4712cb4d4 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4165,10 +4165,11 @@ static PySequenceMethods dictkeys_as_sequence = { static PyObject* dictviews_sub(PyObject* self, PyObject *other) { + PyObject *left = self; if (PyDictKeys_Check(self)) { - self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; } - PyObject *result = PySet_New(self); + PyObject *result = PySet_New(left); if (result == NULL) { return NULL; } @@ -4276,10 +4277,11 @@ _PyDictView_Intersect(PyObject* self, PyObject *other) static PyObject* dictviews_or(PyObject* self, PyObject *other) { + PyObject *left = self; if (PyDictKeys_Check(self)) { - self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; } - PyObject *result = PySet_New(self); + PyObject *result = PySet_New(left); if (result == NULL) { return NULL; } @@ -4299,10 +4301,11 @@ dictviews_or(PyObject* self, PyObject *other) static PyObject* dictviews_xor(PyObject* self, PyObject *other) { + PyObject *left = self; if (PyDictKeys_Check(self)) { - self = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; } - PyObject *result = PySet_New(self); + PyObject *result = PySet_New(left); if (result == NULL) { return NULL; } From 0a8241a80527a08316a108d5f2be1321007ba95c Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 29 Oct 2019 15:44:29 +0900 Subject: [PATCH 3/5] Add news entry --- .../Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst b/Misc/NEWS.d/next/Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst new file mode 100644 index 000000000000000..c001db6679831b0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst @@ -0,0 +1,3 @@ +Optimized some set operations (e.g. ``|``, ``^``, and ``-``) of +``dict_keys``. ``d.keys() | other`` was slower than ``set(d) | other`` but +they are almost same performance for now. From 4430b6e2e7e5844d484fd390a63b8090d7ec089f Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Wed, 30 Oct 2019 16:05:14 +0900 Subject: [PATCH 4/5] Use iter(dict_keys) instead of iter(dict) when dict is subclassed. --- Objects/dictobject.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index d54b6c4712cb4d4..b6f18c1ed223601 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4167,7 +4167,11 @@ dictviews_sub(PyObject* self, PyObject *other) { PyObject *left = self; if (PyDictKeys_Check(self)) { - left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + // PySet_New() has fast path for the dict object. + PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + if (PyDict_CheckExact(dict)) { + left = dict; + } } PyObject *result = PySet_New(left); if (result == NULL) { @@ -4279,7 +4283,11 @@ dictviews_or(PyObject* self, PyObject *other) { PyObject *left = self; if (PyDictKeys_Check(self)) { - left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + // PySet_New() has fast path for the dict object. + PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + if (PyDict_CheckExact(dict)) { + left = dict; + } } PyObject *result = PySet_New(left); if (result == NULL) { @@ -4303,7 +4311,11 @@ dictviews_xor(PyObject* self, PyObject *other) { PyObject *left = self; if (PyDictKeys_Check(self)) { - left = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + // PySet_New() has fast path for the dict object. + PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; + if (PyDict_CheckExact(dict)) { + left = dict; + } } PyObject *result = PySet_New(left); if (result == NULL) { From 86e730b6e0de331562e8803c50926486e5d2acab Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Thu, 7 Nov 2019 16:53:04 +0900 Subject: [PATCH 5/5] code cleanup --- Objects/dictobject.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b6f18c1ed223601..4afa19c8a0a90b1 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4162,8 +4162,11 @@ static PySequenceMethods dictkeys_as_sequence = { (objobjproc)dictkeys_contains, /* sq_contains */ }; +// Create an set object from dictviews object. +// Returns a new reference. +// This utility function is used by set operations. static PyObject* -dictviews_sub(PyObject* self, PyObject *other) +dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyDictKeys_Check(self)) { @@ -4173,7 +4176,13 @@ dictviews_sub(PyObject* self, PyObject *other) left = dict; } } - PyObject *result = PySet_New(left); + return PySet_New(left); +} + +static PyObject* +dictviews_sub(PyObject *self, PyObject *other) +{ + PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } @@ -4281,43 +4290,22 @@ _PyDictView_Intersect(PyObject* self, PyObject *other) static PyObject* dictviews_or(PyObject* self, PyObject *other) { - PyObject *left = self; - if (PyDictKeys_Check(self)) { - // PySet_New() has fast path for the dict object. - PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; - if (PyDict_CheckExact(dict)) { - left = dict; - } - } - PyObject *result = PySet_New(left); + PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } - _Py_IDENTIFIER(update); - PyObject *tmp = _PyObject_CallMethodIdOneArg( - result, &PyId_update, other); - if (tmp == NULL) { + if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } - - Py_DECREF(tmp); return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { - PyObject *left = self; - if (PyDictKeys_Check(self)) { - // PySet_New() has fast path for the dict object. - PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; - if (PyDict_CheckExact(dict)) { - left = dict; - } - } - PyObject *result = PySet_New(left); + PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; }