Skip to content

Commit 56f802e

Browse files
committed
updated for thread-safety, style choices, function choices and benchmark notes
1 parent 4912a05 commit 56f802e

File tree

2 files changed

+22
-12
lines changed

2 files changed

+22
-12
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
Speed up :class:`bytes` creation from :class:`list` and :class:`tuple` by around 81%.
1+
Speed up :class:`bytes` creation from :class:`list` and :class:`tuple` of integers. Benchmarks show that from a list with 1000000 random numbers the time to create a bytes object is reduced by around 31%, or 30% with 10000 numbers, or 27% with 100 numbers.
2+
23
Patch by Ben Hsing

Objects/bytesobject.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat()
77
#include "pycore_call.h" // _PyObject_CallNoArgs()
88
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
9+
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST()
910
#include "pycore_format.h" // F_LJUST
1011
#include "pycore_global_objects.h"// _Py_GET_GLOBAL_OBJECT()
1112
#include "pycore_initconfig.h" // _PyStatus_OK()
@@ -2813,18 +2814,21 @@ static PyObject*
28132814
_PyBytes_FromSequence(PyObject *x)
28142815
{
28152816
Py_ssize_t size = PySequence_Fast_GET_SIZE(x);
2816-
PyObject *bytes = PyBytes_FromStringAndSize(NULL, size);
2817-
if (bytes == NULL)
2817+
PyObject *bytes = _PyBytes_FromSize(size, 0);
2818+
if (bytes == NULL) {
28182819
return NULL;
2819-
char *s = PyBytes_AS_STRING(bytes);
2820-
PyObject **items = PySequence_Fast_ITEMS(x);
2820+
}
2821+
char *str = PyBytes_AS_STRING(bytes);
2822+
PyObject *const *items = PySequence_Fast_ITEMS(x);
2823+
Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(x);
28212824
for (Py_ssize_t i = 0; i < size; i++) {
2822-
if (!PyLong_CheckExact(items[i])) {
2825+
if (!PyLong_Check(items[i])) {
28232826
Py_DECREF(bytes);
2824-
return Py_None; // None as fallback sentinel to the slow path
2827+
/* Py_None as a fallback sentinel to the slow path */
2828+
bytes = Py_None;
2829+
goto done;
28252830
}
2826-
int overflow;
2827-
long value = PyLong_AsLongAndOverflow(items[i], &overflow);
2831+
int value = PyLong_AsInt(items[i]);
28282832
if (value == -1 && PyErr_Occurred()) {
28292833
goto error;
28302834
}
@@ -2834,12 +2838,16 @@ _PyBytes_FromSequence(PyObject *x)
28342838
"bytes must be in range(0, 256)");
28352839
goto error;
28362840
}
2837-
s[i] = (char)value;
2841+
*str++ = (char) value;
28382842
}
2839-
return bytes;
2843+
goto done;
28402844
error:
28412845
Py_DECREF(bytes);
2842-
return NULL;
2846+
bytes = NULL;
2847+
done:
2848+
/* both success and failure need to end the critical section */
2849+
Py_END_CRITICAL_SECTION_SEQUENCE_FAST();
2850+
return bytes;
28432851
}
28442852

28452853
static PyObject *
@@ -2924,6 +2932,7 @@ PyBytes_FromObject(PyObject *x)
29242932

29252933
if (PyList_CheckExact(x) || PyTuple_CheckExact(x)) {
29262934
PyObject *bytes = _PyBytes_FromSequence(x);
2935+
/* Py_None as a fallback sentinel to the slow path */
29272936
if (bytes != Py_None) {
29282937
return bytes;
29292938
}

0 commit comments

Comments
 (0)