Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import sys
import tempfile
import textwrap
lazy import tracemalloc
import threading
import unittest
from _codecs import _unregister_error as _codecs_unregister_error
Expand Down Expand Up @@ -1809,6 +1810,23 @@ def hashing_codec(name):
self.assertEqual(taken, b'Hello')
self.assertEqual(hash(taken), hash(b'Hello'))

@support.cpython_only
@support.bigmemtest(size=support._1G, memuse=1)
def test_unique_temporary_take_memory(self, size):
# Optimization: When passed a unique temporary adopt it rather than
# allocate + copy + deallocate.
def peak(func):
tracemalloc.start()
try:
func()
return tracemalloc.get_traced_memory()[1]
finally:
tracemalloc.stop()
# Copy would be 2.0, 1.5 gives space for other allocations.
self.assertLess(peak(lambda: bytearray(bytes(size))), size * 1.5)
self.assertLess(peak(lambda: bytearray(bytearray(size))), size * 1.5)
self.assertLess(peak(lambda: bytes(bytearray(size))), size * 1.5)

def test_take_bytes_reentrant_resize(self):
# gh-153570: n.__index__() can resize the bytearray, so take_bytes()
# must re-read the size afterwards. It cached the size before the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Speed up :class:`bytes` and :class:`bytearray` construction by adding
:ref:`vectorcall` support.
31 changes: 30 additions & 1 deletion Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ bytearray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}

/*[clinic input]
@vectorcall
bytearray.__init__

source as arg: object = NULL
Expand All @@ -988,7 +989,7 @@ bytearray.__init__
static int
bytearray___init___impl(PyByteArrayObject *self, PyObject *arg,
const char *encoding, const char *errors)
/*[clinic end generated code: output=4ce1304649c2f8b3 input=1141a7122eefd7b9]*/
/*[clinic end generated code: output=4ce1304649c2f8b3 input=24ddb84055f432dd]*/
{
Py_ssize_t count;
PyObject *it;
Expand Down Expand Up @@ -1082,6 +1083,33 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg,
}
}

/* optimization: Adopt bytes rather than copying. */
if (PyBytes_CheckExact(arg)
&& PyUnstable_Object_IsUniqueReferencedTemporary(arg)
&& _PyBytes_GET_CACHED_HASH((PyBytesObject *)arg) == -1)
{
/* Reinit before taking a reference: it asserts the bytes is
uniquely referenced. */
self->ob_bytes_object = arg;
bytearray_reinit_from_bytes(self, PyBytes_GET_SIZE(arg));
Py_INCREF(arg);
return 0;
}

/* optimization: Adopt bytearray rather than copying. */
if (PyByteArray_CheckExact(arg)
&& PyUnstable_Object_IsUniqueReferencedTemporary(arg))
{
PyObject *bytes = bytearray_take_bytes_impl((PyByteArrayObject *)arg,
Py_None);
if (bytes == NULL) {
return -1;
}
self->ob_bytes_object = bytes;
bytearray_reinit_from_bytes(self, PyBytes_GET_SIZE(bytes));
return 0;
}

/* Use the buffer API */
if (PyObject_CheckBuffer(arg)) {
Py_ssize_t size;
Expand Down Expand Up @@ -3027,6 +3055,7 @@ PyTypeObject PyByteArray_Type = {
PyType_GenericAlloc, /* tp_alloc */
bytearray_new, /* tp_new */
PyObject_Free, /* tp_free */
.tp_vectorcall = bytearray_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_BYTEARRAY,
};

Expand Down
9 changes: 8 additions & 1 deletion Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2918,6 +2918,7 @@ static PyObject *
bytes_subtype_new(PyTypeObject *, PyObject *);

/*[clinic input]
@vectorcall
@classmethod
bytes.__new__ as bytes_new

Expand All @@ -2930,7 +2931,7 @@ bytes.__new__ as bytes_new
static PyObject *
bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
const char *errors)
/*[clinic end generated code: output=1e0c471be311a425 input=f0a966d19b7262b4]*/
/*[clinic end generated code: output=1e0c471be311a425 input=b0248d22e221a095]*/
{
PyObject *bytes;
PyObject *func;
Expand Down Expand Up @@ -3002,6 +3003,11 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
bytes = _PyBytes_FromSizeZero(size);
}
}
/* Adopt unique temporary bytearray rather than copying. */
else if (PyByteArray_CheckExact(x)
&& PyUnstable_Object_IsUniqueReferencedTemporary(x)) {
bytes = PyObject_CallMethodNoArgs(x, &_Py_ID(take_bytes));
}
else {
bytes = PyBytes_FromObject(x);
}
Expand Down Expand Up @@ -3316,6 +3322,7 @@ PyTypeObject PyBytes_Type = {
bytes_type_alloc, /* tp_alloc */
bytes_new, /* tp_new */
PyObject_Free, /* tp_free */
.tp_vectorcall = bytes_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_BYTES,
._tp_iteritem = bytes_iteritem,
};
Expand Down
95 changes: 89 additions & 6 deletions Objects/clinic/bytearrayobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 66 additions & 5 deletions Objects/clinic/bytesobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading