diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-22-16-20-16.gh-issue-144157.dxyp7k.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-22-16-20-16.gh-issue-144157.dxyp7k.rst new file mode 100644 index 00000000000000..ff62d739d7804c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-22-16-20-16.gh-issue-144157.dxyp7k.rst @@ -0,0 +1,2 @@ +:meth:`bytes.translate` now allows the compiler to unroll its loop more +usefully for a 2x speedup in the common no-deletions specified case. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 2b0925017f29e4..56de99bde11682 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2237,11 +2237,15 @@ bytes_translate_impl(PyBytesObject *self, PyObject *table, /* If no deletions are required, use faster code */ for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); - if (Py_CHARMASK((*output++ = table_chars[c])) != c) - changed = 1; - } - if (!changed && PyBytes_CheckExact(input_obj)) { - Py_SETREF(result, Py_NewRef(input_obj)); + *output++ = table_chars[c]; + } + /* Check if anything changed (for returning original object) */ + /* We save this check until the end so that the compiler will */ + /* unroll the loop above leading to MUCH faster code. */ + if (PyBytes_CheckExact(input_obj)) { + if (memcmp(PyBytes_AS_STRING(input_obj), output_start, inlen) == 0) { + Py_SETREF(result, Py_NewRef(input_obj)); + } } PyBuffer_Release(&del_table_view); PyBuffer_Release(&table_view);