Skip to content

Commit 26ccdf4

Browse files
author
aleksandr.tseluyko
committed
gh-142659: Optimize set_swap_bodies for intersection_update
Replace the general-purpose set_swap_bodies() with a specialized set_replace_body() that exploits the invariant that src is always a uniquely-referenced temporary about to be discarded.
1 parent bce96a1 commit 26ccdf4

File tree

1 file changed

+48
-54
lines changed

1 file changed

+48
-54
lines changed

Objects/setobject.c

Lines changed: 48 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,74 +1475,68 @@ copy_small_table(setentry *dest, setentry *src)
14751475
}
14761476
#endif
14771477

1478-
/* set_swap_bodies() switches the contents of any two sets by moving their
1479-
internal data pointers and, if needed, copying the internal smalltables.
1480-
Semantically equivalent to:
1478+
/* set_replace_body() replaces the contents of dst with those of src,
1479+
moving dst's old contents into src for proper cleanup on Py_DECREF.
14811480
1482-
t=set(a); a.clear(); a.update(b); b.clear(); b.update(t); del t
1481+
The caller guarantees that src is a uniquely-referenced temporary set
1482+
that will be discarded immediately afterward. This allows us to skip
1483+
atomic operations and shared-marking on src's fields, and to skip the
1484+
frozenset hash swap (neither argument is ever a frozenset here).
14831485
14841486
The function always succeeds and it leaves both objects in a stable state.
1485-
Useful for operations that update in-place (by allowing an intermediate
1486-
result to be swapped into one of the original inputs).
14871487
*/
14881488

14891489
static void
1490-
set_swap_bodies(PySetObject *a, PySetObject *b)
1490+
set_replace_body(PySetObject *dst, PySetObject *src)
14911491
{
14921492
Py_ssize_t t;
14931493
setentry *u;
14941494
setentry tab[PySet_MINSIZE];
1495-
Py_hash_t h;
1496-
1497-
setentry *a_table = a->table;
1498-
setentry *b_table = b->table;
1499-
FT_ATOMIC_STORE_PTR_RELEASE(a->table, NULL);
1500-
FT_ATOMIC_STORE_PTR_RELEASE(b->table, NULL);
1501-
1502-
t = a->fill; a->fill = b->fill; b->fill = t;
1503-
t = a->used;
1504-
FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used);
1505-
FT_ATOMIC_STORE_SSIZE_RELAXED(b->used, t);
1506-
t = a->mask;
1507-
FT_ATOMIC_STORE_SSIZE_RELEASE(a->mask, b->mask);
1508-
FT_ATOMIC_STORE_SSIZE_RELEASE(b->mask, t);
1509-
1510-
u = a_table;
1511-
if (a_table == a->smalltable)
1512-
u = b->smalltable;
1513-
a_table = b_table;
1514-
if (b_table == b->smalltable)
1515-
a_table = a->smalltable;
1516-
b_table = u;
1517-
1518-
if (a_table == a->smalltable || b_table == b->smalltable) {
1519-
memcpy(tab, a->smalltable, sizeof(tab));
1495+
1496+
assert(!PyType_IsSubtype(Py_TYPE(dst), &PyFrozenSet_Type));
1497+
assert(!PyType_IsSubtype(Py_TYPE(src), &PyFrozenSet_Type));
1498+
assert(Py_REFCNT(src) == 1);
1499+
1500+
setentry *dst_table = dst->table;
1501+
setentry *src_table = src->table;
1502+
FT_ATOMIC_STORE_PTR_RELEASE(dst->table, NULL);
1503+
src->table = NULL;
1504+
1505+
t = dst->fill; dst->fill = src->fill; src->fill = t;
1506+
t = dst->used;
1507+
FT_ATOMIC_STORE_SSIZE_RELAXED(dst->used, src->used);
1508+
src->used = t;
1509+
t = dst->mask;
1510+
FT_ATOMIC_STORE_SSIZE_RELEASE(dst->mask, src->mask);
1511+
src->mask = t;
1512+
1513+
u = dst_table;
1514+
if (dst_table == dst->smalltable)
1515+
u = src->smalltable;
1516+
dst_table = src_table;
1517+
if (src_table == src->smalltable)
1518+
dst_table = dst->smalltable;
1519+
src_table = u;
1520+
1521+
if (dst_table == dst->smalltable || src_table == src->smalltable) {
1522+
memcpy(tab, dst->smalltable, sizeof(tab));
15201523
#ifndef Py_GIL_DISABLED
1521-
memcpy(a->smalltable, b->smalltable, sizeof(tab));
1522-
memcpy(b->smalltable, tab, sizeof(tab));
1524+
memcpy(dst->smalltable, src->smalltable, sizeof(tab));
1525+
memcpy(src->smalltable, tab, sizeof(tab));
15231526
#else
1524-
copy_small_table(a->smalltable, b->smalltable);
1525-
copy_small_table(b->smalltable, tab);
1527+
copy_small_table(dst->smalltable, src->smalltable);
1528+
memcpy(src->smalltable, tab, sizeof(tab));
15261529
#endif
15271530
}
15281531

1529-
if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type) &&
1530-
PyType_IsSubtype(Py_TYPE(b), &PyFrozenSet_Type)) {
1531-
h = FT_ATOMIC_LOAD_SSIZE_RELAXED(a->hash);
1532-
FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, FT_ATOMIC_LOAD_SSIZE_RELAXED(b->hash));
1533-
FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, h);
1534-
} else {
1535-
FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, -1);
1536-
FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, -1);
1537-
}
1538-
if (!SET_IS_SHARED(b) && SET_IS_SHARED(a)) {
1539-
SET_MARK_SHARED(b);
1540-
}
1541-
if (!SET_IS_SHARED(a) && SET_IS_SHARED(b)) {
1542-
SET_MARK_SHARED(a);
1532+
FT_ATOMIC_STORE_SSIZE_RELAXED(dst->hash, -1);
1533+
1534+
if (SET_IS_SHARED(dst)) {
1535+
SET_MARK_SHARED(src);
15431536
}
1544-
FT_ATOMIC_STORE_PTR_RELEASE(a->table, a_table);
1545-
FT_ATOMIC_STORE_PTR_RELEASE(b->table, b_table);
1537+
1538+
FT_ATOMIC_STORE_PTR_RELEASE(dst->table, dst_table);
1539+
src->table = src_table;
15461540
}
15471541

15481542
/*[clinic input]
@@ -1797,7 +1791,7 @@ set_intersection_update(PySetObject *so, PyObject *other)
17971791
tmp = set_intersection(so, other);
17981792
if (tmp == NULL)
17991793
return NULL;
1800-
set_swap_bodies(so, (PySetObject *)tmp);
1794+
set_replace_body(so, (PySetObject *)tmp);
18011795
Py_DECREF(tmp);
18021796
Py_RETURN_NONE;
18031797
}
@@ -1821,7 +1815,7 @@ set_intersection_update_multi_impl(PySetObject *so, PyObject * const *others,
18211815
if (tmp == NULL)
18221816
return NULL;
18231817
Py_BEGIN_CRITICAL_SECTION(so);
1824-
set_swap_bodies(so, (PySetObject *)tmp);
1818+
set_replace_body(so, (PySetObject *)tmp);
18251819
Py_END_CRITICAL_SECTION();
18261820
Py_DECREF(tmp);
18271821
Py_RETURN_NONE;

0 commit comments

Comments
 (0)