Skip to content

Commit 242b41f

Browse files
Specialize FOR_ITER for tuples
1 parent cd67c1b commit 242b41f

9 files changed

Lines changed: 121 additions & 67 deletions

File tree

Include/internal/pycore_opcode.h

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

Include/internal/pycore_tuple.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ struct _Py_tuple_state {
6767
extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
6868
extern PyObject *_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
6969

70+
71+
typedef struct {
72+
PyObject_HEAD
73+
Py_ssize_t it_index;
74+
PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */
75+
} _PyTupleIterObject;
76+
7077
#ifdef __cplusplus
7178
}
7279
#endif

Include/opcode.h

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

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ def pseudo_op(name, op, real_ops):
320320
],
321321
"FOR_ITER": [
322322
"FOR_ITER_LIST",
323+
"FOR_ITER_TUPLE",
323324
"FOR_ITER_RANGE",
324325
"FOR_ITER_GEN",
325326
],

Objects/tupleobject.c

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -995,29 +995,24 @@ _PyTuple_ClearFreeList(PyInterpreterState *interp)
995995

996996
/*********************** Tuple Iterator **************************/
997997

998-
typedef struct {
999-
PyObject_HEAD
1000-
Py_ssize_t it_index;
1001-
PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */
1002-
} tupleiterobject;
1003998

1004999
static void
1005-
tupleiter_dealloc(tupleiterobject *it)
1000+
tupleiter_dealloc(_PyTupleIterObject *it)
10061001
{
10071002
_PyObject_GC_UNTRACK(it);
10081003
Py_XDECREF(it->it_seq);
10091004
PyObject_GC_Del(it);
10101005
}
10111006

10121007
static int
1013-
tupleiter_traverse(tupleiterobject *it, visitproc visit, void *arg)
1008+
tupleiter_traverse(_PyTupleIterObject *it, visitproc visit, void *arg)
10141009
{
10151010
Py_VISIT(it->it_seq);
10161011
return 0;
10171012
}
10181013

10191014
static PyObject *
1020-
tupleiter_next(tupleiterobject *it)
1015+
tupleiter_next(_PyTupleIterObject *it)
10211016
{
10221017
PyTupleObject *seq;
10231018
PyObject *item;
@@ -1040,7 +1035,7 @@ tupleiter_next(tupleiterobject *it)
10401035
}
10411036

10421037
static PyObject *
1043-
tupleiter_len(tupleiterobject *it, PyObject *Py_UNUSED(ignored))
1038+
tupleiter_len(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored))
10441039
{
10451040
Py_ssize_t len = 0;
10461041
if (it->it_seq)
@@ -1051,7 +1046,7 @@ tupleiter_len(tupleiterobject *it, PyObject *Py_UNUSED(ignored))
10511046
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
10521047

10531048
static PyObject *
1054-
tupleiter_reduce(tupleiterobject *it, PyObject *Py_UNUSED(ignored))
1049+
tupleiter_reduce(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored))
10551050
{
10561051
if (it->it_seq)
10571052
return Py_BuildValue("N(O)n", _PyEval_GetBuiltin(&_Py_ID(iter)),
@@ -1061,7 +1056,7 @@ tupleiter_reduce(tupleiterobject *it, PyObject *Py_UNUSED(ignored))
10611056
}
10621057

10631058
static PyObject *
1064-
tupleiter_setstate(tupleiterobject *it, PyObject *state)
1059+
tupleiter_setstate(_PyTupleIterObject *it, PyObject *state)
10651060
{
10661061
Py_ssize_t index = PyLong_AsSsize_t(state);
10671062
if (index == -1 && PyErr_Occurred())
@@ -1089,7 +1084,7 @@ static PyMethodDef tupleiter_methods[] = {
10891084
PyTypeObject PyTupleIter_Type = {
10901085
PyVarObject_HEAD_INIT(&PyType_Type, 0)
10911086
"tuple_iterator", /* tp_name */
1092-
sizeof(tupleiterobject), /* tp_basicsize */
1087+
sizeof(_PyTupleIterObject), /* tp_basicsize */
10931088
0, /* tp_itemsize */
10941089
/* methods */
10951090
(destructor)tupleiter_dealloc, /* tp_dealloc */
@@ -1122,13 +1117,13 @@ PyTypeObject PyTupleIter_Type = {
11221117
static PyObject *
11231118
tuple_iter(PyObject *seq)
11241119
{
1125-
tupleiterobject *it;
1120+
_PyTupleIterObject *it;
11261121

11271122
if (!PyTuple_Check(seq)) {
11281123
PyErr_BadInternalCall();
11291124
return NULL;
11301125
}
1131-
it = PyObject_GC_New(tupleiterobject, &PyTupleIter_Type);
1126+
it = PyObject_GC_New(_PyTupleIterObject, &PyTupleIter_Type);
11321127
if (it == NULL)
11331128
return NULL;
11341129
it->it_index = 0;

Python/bytecodes.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,6 +2615,29 @@ dummy_func(
26152615
end_for_iter_list:
26162616
}
26172617

2618+
// stack effect: ( -- __0)
2619+
inst(FOR_ITER_TUPLE) {
2620+
assert(cframe.use_tracing == 0);
2621+
_PyTupleIterObject *it = (_PyTupleIterObject *)TOP();
2622+
DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER);
2623+
STAT_INC(FOR_ITER, hit);
2624+
PyTupleObject *seq = it->it_seq;
2625+
if (seq) {
2626+
if (it->it_index < PyTuple_GET_SIZE(seq)) {
2627+
PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++);
2628+
PUSH(Py_NewRef(next));
2629+
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
2630+
goto end_for_iter_tuple; // End of this instruction
2631+
}
2632+
it->it_seq = NULL;
2633+
Py_DECREF(seq);
2634+
}
2635+
STACK_SHRINK(1);
2636+
Py_DECREF(it);
2637+
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
2638+
end_for_iter_tuple:
2639+
}
2640+
26182641
// stack effect: ( -- __0)
26192642
inst(FOR_ITER_RANGE) {
26202643
assert(cframe.use_tracing == 0);

Python/generated_cases.c.h

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

0 commit comments

Comments
 (0)