]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-143192 Avoid incref/decref pair in long_bitwise (gh-143194) main
authorPieter Eendebak <pieter.eendebak@gmail.com>
Thu, 29 Jan 2026 21:32:09 +0000 (22:32 +0100)
committerGitHub <noreply@github.com>
Thu, 29 Jan 2026 21:32:09 +0000 (16:32 -0500)
Remove unnecessary reference count operations in long_bitwise in order to
avoid reference count contention in the free-threading build.

Misc/NEWS.d/next/Core_and_Builtins/2025-12-29-19-31-46.gh-issue-143192.JxGAyl.rst [new file with mode: 0644]
Objects/longobject.c

diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-29-19-31-46.gh-issue-143192.JxGAyl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-29-19-31-46.gh-issue-143192.JxGAyl.rst
new file mode 100644 (file)
index 0000000..3a99b3d
--- /dev/null
@@ -0,0 +1 @@
+Improve performance of bitwise operations on multi-digit ints.
index 74958cb8b9bbb03d3e9032d9891d0b8b1a12e9bb..7ce5d0535b884ea639784d8547555bee2e3a5a52 100644 (file)
@@ -5589,46 +5589,45 @@ long_bitwise(PyLongObject *a,
     Py_ssize_t size_a, size_b, size_z, i;
     PyLongObject *z;
 
+    PyLongObject *new_a = NULL;
+    PyLongObject *new_b = NULL;
+
     /* Bitwise operations for negative numbers operate as though
        on a two's complement representation.  So convert arguments
        from sign-magnitude to two's complement, and convert the
        result back to sign-magnitude at the end. */
 
-    /* If a is negative, replace it by its two's complement. */
     size_a = _PyLong_DigitCount(a);
+    size_b = _PyLong_DigitCount(b);
+    /* Swap a and b if necessary to ensure size_a >= size_b. */
+    if (size_a < size_b) {
+        z = a; a = b; b = z;
+        size_z = size_a; size_a = size_b; size_b = size_z;
+    }
+
+    /* If a is negative, replace it by its two's complement. */
     nega = _PyLong_IsNegative(a);
     if (nega) {
         z = long_alloc(size_a);
         if (z == NULL)
             return NULL;
         v_complement(z->long_value.ob_digit, a->long_value.ob_digit, size_a);
+        new_a = z; // reference to decrement instead of a itself
         a = z;
     }
-    else
-        /* Keep reference count consistent. */
-        Py_INCREF(a);
 
     /* Same for b. */
-    size_b = _PyLong_DigitCount(b);
     negb = _PyLong_IsNegative(b);
     if (negb) {
         z = long_alloc(size_b);
         if (z == NULL) {
-            Py_DECREF(a);
+            Py_XDECREF(new_a);
             return NULL;
         }
         v_complement(z->long_value.ob_digit, b->long_value.ob_digit, size_b);
+        new_b = z; // reference to decrement instead of b itself
         b = z;
     }
-    else
-        Py_INCREF(b);
-
-    /* Swap a and b if necessary to ensure size_a >= size_b. */
-    if (size_a < size_b) {
-        z = a; a = b; b = z;
-        size_z = size_a; size_a = size_b; size_b = size_z;
-        negz = nega; nega = negb; negb = negz;
-    }
 
     /* JRH: The original logic here was to allocate the result value (z)
        as the longer of the two operands.  However, there are some cases
@@ -5658,8 +5657,8 @@ long_bitwise(PyLongObject *a,
        the final two's complement of z doesn't overflow. */
     z = long_alloc(size_z + negz);
     if (z == NULL) {
-        Py_DECREF(a);
-        Py_DECREF(b);
+        Py_XDECREF(new_a);
+        Py_XDECREF(new_b);
         return NULL;
     }
 
@@ -5696,8 +5695,8 @@ long_bitwise(PyLongObject *a,
         v_complement(z->long_value.ob_digit, z->long_value.ob_digit, size_z+1);
     }
 
-    Py_DECREF(a);
-    Py_DECREF(b);
+    Py_XDECREF(new_a);
+    Py_XDECREF(new_b);
     return (PyObject *)maybe_small_long(long_normalize(z));
 }