From 7c2bb0651c1854f6ecc0c081c83c5281cd7abb20 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Wed, 28 Oct 2026 01:06:03 -0700 Subject: [PATCH 1/2] gh-160931: Speed up bytes construction from exact bytes and int Since 3.11 added `bytes.__bytes__`, bytes(b) for an exact bytes binds and calls that method only to get b back. Return an exact bytes argument directly, like PyObject_Bytes() and PyBytes_FromObject() do, and skip the lookup, which always misses, for an exact int. How bytes(x) resolves is unchanged: both types are immutable, so the skipped lookup and call could only ever produce the same result, and subclasses, including bool, still look up `__bytes__` first. These cases just get there faster. pyperf, PGO+LTO, interleaved A/B on 19270d3bbb5 (A/A spread <= 1.05x): | Benchmark | main | patch | |-------------------|:-------:|:---------------------:| | bytes(b) | 28.5 ns | 12.8 ns: 2.22x faster | | Sub(b) | 51.7 ns | 36.0 ns: 1.44x faster | | bytes(16) | 23.5 ns | 21.0 ns: 1.12x faster | | bytes(hb) | 38.7 ns | 38.0 ns: 1.02x faster | | bytes(s, 'ascii') | 26.5 ns | 27.1 ns: 1.02x slower | | bytes() | 10.7 ns | 11.1 ns: 1.03x slower | Not significant: bytes(ba), bytes(l), bytes(mv). The small slowdowns (1.02-1.03x) are within the noise on my machine. import pyperf runner = pyperf.Runner() for stmt in ["bytes(b)", "bytes(16)", "bytes(ba)", "bytes(mv)", "bytes(l)", "bytes(s, 'ascii')", "bytes(hb)", "Sub(b)", "bytes()"]: runner.timeit(stmt, stmt, "b = b'abcdefghijklmnop'; ba = bytearray(b); mv = memoryview(b); " "l = list(range(16)); s = 'abcdefghijklmnop'\n" "class Sub(bytes): pass\n" "class HB:\n def __bytes__(self): return b\n" "hb = HB()") Co-Authored-By: Claude Opus 5.5 --- .../2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst | 2 ++ Objects/bytesobject.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst new file mode 100644 index 000000000000000..b45654bf0efb2c8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst @@ -0,0 +1,2 @@ +Speed up :class:`bytes` construction from an exact :class:`bytes` or +:class:`int` argument by skipping the :meth:`~object.__bytes__` lookup. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index e9264f765a08e6d..e5d25a2fd8a5c8d 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2970,10 +2970,16 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, "errors without a string argument"); return NULL; } + /* bytes is immutable and bytes.__bytes__() returns an exact bytes + unchanged, so skip the lookup and the call. */ + else if (PyBytes_CheckExact(x)) { + bytes = Py_NewRef(x); + } /* We'd like to call PyObject_Bytes here, but we need to check for an integer argument before deferring to PyBytes_FromObject, something - PyObject_Bytes doesn't do. */ - else if ((func = _PyObject_LookupSpecial(x, &_Py_ID(__bytes__))) != NULL) { + PyObject_Bytes doesn't do. int is immutable and has no __bytes__. */ + else if (!PyLong_CheckExact(x) && + (func = _PyObject_LookupSpecial(x, &_Py_ID(__bytes__))) != NULL) { bytes = _PyObject_CallNoArgs(func); Py_DECREF(func); if (bytes == NULL) From 6805424097a56a40859fe6bc83003ac15b36f247 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 29 Oct 2026 15:16:14 -0700 Subject: [PATCH 2/2] Remove NEWS --- .../2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst deleted file mode 100644 index b45654bf0efb2c8..000000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-29-14-46-18.gh-issue-160931.Fk4RhD.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up :class:`bytes` construction from an exact :class:`bytes` or -:class:`int` argument by skipping the :meth:`~object.__bytes__` lookup.