From f58a7c717584241467970623384ce61cbd776f29 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 30 Aug 2025 21:25:36 +0200 Subject: [PATCH 1/2] gh-132657: avoid locks and refcounting in `frozenset` lookups (#136107) --- ...-07-09-21-27-14.gh-issue-132657.kSA8R3.rst | 1 + Objects/setobject.c | 35 +++++++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-21-27-14.gh-issue-132657.kSA8R3.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-21-27-14.gh-issue-132657.kSA8R3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-21-27-14.gh-issue-132657.kSA8R3.rst new file mode 100644 index 00000000000000..99f7a990875a0a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-21-27-14.gh-issue-132657.kSA8R3.rst @@ -0,0 +1 @@ +Improve performance of :class:`frozenset` by removing locks in the free-threading build. diff --git a/Objects/setobject.c b/Objects/setobject.c index 63ce55e236546f..d8340499be5aae 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -86,6 +86,8 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash) int probes; int cmp; + int frozenset = PyFrozenSet_CheckExact(so); + while (1) { entry = &so->table[i]; probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0; @@ -102,13 +104,20 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash) && unicode_eq(startkey, key)) return entry; table = so->table; - Py_INCREF(startkey); - cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); - Py_DECREF(startkey); - if (cmp < 0) - return NULL; - if (table != so->table || entry->key != startkey) - return set_lookkey(so, key, hash); + if (frozenset) { + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + if (cmp < 0) + return NULL; + } else { + // incref startkey because it can be removed from the set by the compare + Py_INCREF(startkey); + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (table != so->table || entry->key != startkey) + return set_lookkey(so, key, hash); + } if (cmp > 0) return entry; mask = so->mask; @@ -2235,10 +2244,16 @@ set_contains_lock_held(PySetObject *so, PyObject *key) int _PySet_Contains(PySetObject *so, PyObject *key) { + assert(so); + int rv; - Py_BEGIN_CRITICAL_SECTION(so); - rv = set_contains_lock_held(so, key); - Py_END_CRITICAL_SECTION(); + if (PyFrozenSet_CheckExact(so)) { + rv = set_contains_lock_held(so, key); + } else { + Py_BEGIN_CRITICAL_SECTION(so); + rv = set_contains_lock_held(so, key); + Py_END_CRITICAL_SECTION(); + } return rv; } From d3d94e0ed715829d9bf93ef9c35e04832962f19f Mon Sep 17 00:00:00 2001 From: alm Date: Sun, 31 Aug 2025 00:21:25 +0300 Subject: [PATCH 2/2] gh-138061: Exclude __pycache__ directory from the computed digest in the JIT stencils (#138131) Exclude the __pycache__ directory when generating the digest in the JIT stencils --- .../next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst | 1 + Tools/jit/_targets.py | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst diff --git a/Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst b/Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst new file mode 100644 index 00000000000000..7af79d0b87ef55 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst @@ -0,0 +1 @@ +Ensure reproducible builds by making JIT stencil header generation deterministic. diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 7e261c9f8e297f..c3ce24643fd4a6 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -71,6 +71,9 @@ def _compute_digest(self) -> str: hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes()) hasher.update((self.pyconfig_dir / "pyconfig.h").read_bytes()) for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)): + # Exclude cache files from digest computation to ensure reproducible builds. + if dirpath.endswith("__pycache__"): + continue for filename in filenames: hasher.update(pathlib.Path(dirpath, filename).read_bytes()) return hasher.hexdigest()