Skip to content

Commit 9f054d8

Browse files
committed
gh-158600: Avoid copying in set.intersection_update()
Calculate all intersections before swapping the final result into the target. Keep the calculation and swap within the same critical section so concurrent updates are not lost. This keeps the existing unchanged-on-error behavior while removing the O(len(self)) initial copy.
1 parent add447e commit 9f054d8

1 file changed

Lines changed: 25 additions & 9 deletions

File tree

‎Objects/setobject.c‎

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,20 +1861,36 @@ set_intersection_update_multi_impl(PySetObject *so, PyObject * const *others,
18611861
Py_ssize_t others_length)
18621862
/*[clinic end generated code: output=d768b5584675b48d input=782e422fc370e4fc]*/
18631863
{
1864-
PyObject *copy;
1865-
PyObject *result = NULL;
1864+
Py_ssize_t i;
1865+
PyObject *other;
1866+
PyObject *result;
18661867

1867-
Py_BEGIN_CRITICAL_SECTION(so);
1868-
copy = set_copy_untracked_lock_held(so);
1869-
if (copy != NULL) {
1870-
result = set_intersection_multi_impl((PySetObject *)copy,
1871-
others, others_length);
1872-
Py_DECREF(copy);
1868+
if (others_length == 0) {
1869+
Py_RETURN_NONE;
1870+
}
1871+
1872+
other = others[0];
1873+
Py_BEGIN_CRITICAL_SECTION2(so, other);
1874+
result = set_intersection(so, other);
1875+
if (result != NULL) {
1876+
for (i = 1; i < others_length; i++) {
1877+
PyObject *newresult;
1878+
1879+
other = others[i];
1880+
Py_BEGIN_CRITICAL_SECTION(other);
1881+
newresult = set_intersection((PySetObject *)result, other);
1882+
Py_END_CRITICAL_SECTION();
1883+
if (newresult == NULL) {
1884+
Py_CLEAR(result);
1885+
break;
1886+
}
1887+
Py_SETREF(result, newresult);
1888+
}
18731889
if (result != NULL) {
18741890
set_swap_bodies(so, (PySetObject *)result);
18751891
}
18761892
}
1877-
Py_END_CRITICAL_SECTION();
1893+
Py_END_CRITICAL_SECTION2();
18781894

18791895
if (result == NULL) {
18801896
return NULL;

0 commit comments

Comments
 (0)