[Python-checkins] bpo-38613: Optimize set operations of dict keys. (GH-16961)

Inada Naoki webhook-mailer at python.org
Thu Nov 7 10:59:11 EST 2019


https://github.com/python/cpython/commit/6cbc84fb99acb33dd659d7adb29a20adbe62b74a
commit: 6cbc84fb99acb33dd659d7adb29a20adbe62b74a
branch: master
author: Inada Naoki <songofacandy at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-11-08T00:59:04+09:00
summary:

bpo-38613: Optimize set operations of dict keys. (GH-16961)

files:
A Misc/NEWS.d/next/Core and Builtins/2019-10-29-15-44-24.bpo-38613.V_R3NC.rst
M Objects/dictobject.c

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 0000000000000..c001db6679831
--- /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.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index d909f220a984b..4afa19c8a0a90 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -4162,17 +4162,34 @@ 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 *result = PySet_New(self);
-    PyObject *tmp;
-    _Py_IDENTIFIER(difference_update);
+    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;
+        }
+    }
+    return PySet_New(left);
+}
 
-    if (result == NULL)
+static PyObject*
+dictviews_sub(PyObject *self, PyObject *other)
+{
+    PyObject *result = dictviews_to_set(self);
+    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,34 +4290,29 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
 static PyObject*
 dictviews_or(PyObject* self, PyObject *other)
 {
-    PyObject *result = PySet_New(self);
-    PyObject *tmp;
-    _Py_IDENTIFIER(update);
-
-    if (result == NULL)
+    PyObject *result = dictviews_to_set(self);
+    if (result == NULL) {
         return NULL;
+    }
 
-    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 *result = PySet_New(self);
-    PyObject *tmp;
-    _Py_IDENTIFIER(symmetric_difference_update);
-
-    if (result == NULL)
+    PyObject *result = dictviews_to_set(self);
+    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;



More information about the Python-checkins mailing list