Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 107863e

Browse files
authored
gh-144569: Avoid creating temporary objects in BINARY_SLICE for list, tuple, and unicode (GH-144590)
* Scalar replacement of BINARY_SLICE for list, tuple, and unicode
1 parent 1cf5abe commit 107863e

File tree

13 files changed

+253
-74
lines changed

13 files changed

+253
-74
lines changed

Include/cpython/ceval.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ _PyEval_RequestCodeExtraIndex(freefunc f) {
2323

2424
PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
2525
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
26+
PyAPI_FUNC(int) _PyEval_UnpackIndices(PyObject *, PyObject *,
27+
Py_ssize_t,
28+
Py_ssize_t *, Py_ssize_t *);
2629

2730

2831
// Trampoline API

Include/internal/pycore_list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern "C" {
1414

1515
PyAPI_FUNC(PyObject*) _PyList_Extend(PyListObject *, PyObject *);
1616
PyAPI_FUNC(PyObject) *_PyList_SliceSubscript(PyObject*, PyObject*);
17+
PyAPI_FUNC(PyObject *) _PyList_BinarySlice(PyObject *, PyObject *, PyObject *);
1718
extern void _PyList_DebugMallocStats(FILE *out);
1819
// _PyList_GetItemRef should be used only when the object is known as a list
1920
// because it doesn't raise TypeError when the object is not a list, whereas PyList_GetItemRef does.

Include/internal/pycore_tuple.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);
2525

2626
PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefStealOnSuccess(const union _PyStackRef *, Py_ssize_t);
2727
PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
28+
PyAPI_FUNC(PyObject *) _PyTuple_BinarySlice(PyObject *, PyObject *, PyObject *);
2829

2930
typedef struct {
3031
PyObject_HEAD

Include/internal/pycore_unicodeobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extern PyObject* _PyUnicode_ResizeCompact(
3232
PyObject *unicode,
3333
Py_ssize_t length);
3434
extern PyObject* _PyUnicode_GetEmpty(void);
35+
PyAPI_FUNC(PyObject*) _PyUnicode_BinarySlice(PyObject *, PyObject *, PyObject *);
3536

3637

3738
/* Generic helper macro to convert characters of different types.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimize ``BINARY_SLICE`` for list, tuple, and unicode by avoiding temporary ``slice`` object creation.

Modules/_testinternalcapi/test_cases.c.h

Lines changed: 40 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/listobject.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,30 @@ list_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
716716
return (PyObject *)np;
717717
}
718718

719+
PyObject *
720+
_PyList_BinarySlice(PyObject *container, PyObject *start, PyObject *stop)
721+
{
722+
assert(PyList_CheckExact(container));
723+
Py_ssize_t istart = 0;
724+
Py_ssize_t istop = PY_SSIZE_T_MAX;
725+
/* Unpack the index values before acquiring the lock, since
726+
* _PyEval_SliceIndex may call __index__ which could execute
727+
* arbitrary Python code. */
728+
if (!_PyEval_SliceIndex(start, &istart)) {
729+
return NULL;
730+
}
731+
if (!_PyEval_SliceIndex(stop, &istop)) {
732+
return NULL;
733+
}
734+
PyObject *ret;
735+
Py_BEGIN_CRITICAL_SECTION(container);
736+
Py_ssize_t len = Py_SIZE(container);
737+
PySlice_AdjustIndices(len, &istart, &istop, 1);
738+
ret = list_slice_lock_held((PyListObject *)container, istart, istop);
739+
Py_END_CRITICAL_SECTION();
740+
return ret;
741+
}
742+
719743
PyObject *
720744
PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
721745
{

Objects/tupleobject.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,25 @@ tuple_slice(PyTupleObject *a, Py_ssize_t ilow,
472472
return PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow);
473473
}
474474

475+
PyObject *
476+
_PyTuple_BinarySlice(PyObject *container, PyObject *start, PyObject *stop)
477+
{
478+
assert(PyTuple_CheckExact(container));
479+
Py_ssize_t len = Py_SIZE(container);
480+
Py_ssize_t istart, istop;
481+
if (!_PyEval_UnpackIndices(start, stop, len, &istart, &istop)) {
482+
return NULL;
483+
}
484+
if (istart == 0 && istop == len) {
485+
return Py_NewRef(container);
486+
}
487+
if (istop < istart) {
488+
istop = istart;
489+
}
490+
return PyTuple_FromArray(((PyTupleObject *)container)->ob_item + istart,
491+
istop - istart);
492+
}
493+
475494
PyObject *
476495
PyTuple_GetSlice(PyObject *op, Py_ssize_t i, Py_ssize_t j)
477496
{

Objects/unicodeobject.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12312,6 +12312,18 @@ _PyUnicode_XStrip(PyObject *self, int striptype, PyObject *sepobj)
1231212312
return PyUnicode_Substring(self, i, j);
1231312313
}
1231412314

12315+
PyObject*
12316+
_PyUnicode_BinarySlice(PyObject *container, PyObject *start_o, PyObject *stop_o)
12317+
{
12318+
assert(PyUnicode_CheckExact(container));
12319+
Py_ssize_t len = PyUnicode_GET_LENGTH(container);
12320+
Py_ssize_t istart, istop;
12321+
if (!_PyEval_UnpackIndices(start_o, stop_o, len, &istart, &istop)) {
12322+
return NULL;
12323+
}
12324+
return PyUnicode_Substring(container, istart, istop);
12325+
}
12326+
1231512327
PyObject*
1231612328
PyUnicode_Substring(PyObject *self, Py_ssize_t start, Py_ssize_t end)
1231712329
{

Python/bytecodes.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -866,19 +866,30 @@ dummy_func(
866866
}
867867

868868
op(_BINARY_SLICE, (container, start, stop -- res)) {
869-
PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start),
870-
PyStackRef_AsPyObjectSteal(stop));
869+
PyObject *container_o = PyStackRef_AsPyObjectBorrow(container);
870+
PyObject *start_o = PyStackRef_AsPyObjectBorrow(start);
871+
PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop);
871872
PyObject *res_o;
872-
// Can't use ERROR_IF() here, because we haven't
873-
// DECREF'ed container yet, and we still own slice.
874-
if (slice == NULL) {
875-
res_o = NULL;
873+
if (PyList_CheckExact(container_o)) {
874+
res_o = _PyList_BinarySlice(container_o, start_o, stop_o);
875+
}
876+
else if (PyTuple_CheckExact(container_o)) {
877+
res_o = _PyTuple_BinarySlice(container_o, start_o, stop_o);
878+
}
879+
else if (PyUnicode_CheckExact(container_o)) {
880+
res_o = _PyUnicode_BinarySlice(container_o, start_o, stop_o);
876881
}
877882
else {
878-
res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice);
879-
Py_DECREF(slice);
883+
PyObject *slice = PySlice_New(start_o, stop_o, NULL);
884+
if (slice == NULL) {
885+
res_o = NULL;
886+
}
887+
else {
888+
res_o = PyObject_GetItem(container_o, slice);
889+
Py_DECREF(slice);
890+
}
880891
}
881-
PyStackRef_CLOSE(container);
892+
DECREF_INPUTS();
882893
ERROR_IF(res_o == NULL);
883894
res = PyStackRef_FromPyObjectSteal(res_o);
884895
}

0 commit comments

Comments
 (0)