From 2fa0b0d756ec0ad5bd66be48dcf6411e879ffb73 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Fri, 27 Aug 2021 06:03:46 +0300 Subject: [PATCH 1/8] bpo-45026: More compact range iterator --- Lib/test/test_range.py | 18 ++++++++++ Objects/rangeobject.c | 77 ++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 107c0e2e11c7ce..9cc864af874633 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -421,6 +421,24 @@ def test_large_exhausted_iterator_pickling(self): self.assertEqual(list(i), []) self.assertEqual(list(i2), []) + def test_iterator_unpickle_compat(self): + testcases = [ + b'c__builtin__\niter\n(c__builtin__\nxrange\n(I10\nI20\nI2\ntRtRI2\nb.', + b'c__builtin__\niter\n(c__builtin__\nxrange\n(K\nK\x14K\x02tRtRK\x02b.', + b'\x80\x02c__builtin__\niter\nc__builtin__\nxrange\nK\nK\x14K\x02\x87R\x85RK\x02b.', + b'\x80\x03cbuiltins\niter\ncbuiltins\nrange\nK\nK\x14K\x02\x87R\x85RK\x02b.', + b'\x80\x04\x951\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x8c\x04iter\x93\x8c\x08builtins\x8c\x05range\x93K\nK\x14K\x02\x87R\x85RK\x02b.', + + b'c__builtin__\niter\n(c__builtin__\nxrange\n(L-36893488147419103232L\nI20\nI2\ntRtRL18446744073709551623L\nb.', + b'c__builtin__\niter\n(c__builtin__\nxrange\n(L-36893488147419103232L\nK\x14K\x02tRtRL18446744073709551623L\nb.', + b'\x80\x02c__builtin__\niter\nc__builtin__\nxrange\n\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.', + b'\x80\x03cbuiltins\niter\ncbuiltins\nrange\n\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.', + b'\x80\x04\x95C\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x8c\x04iter\x93\x8c\x08builtins\x8c\x05range\x93\x8a\t\x00\x00\x00\x00\x00\x00\x00\x00\xfeK\x14K\x02\x87R\x85R\x8a\t\x07\x00\x00\x00\x00\x00\x00\x00\x01b.', + ] + for t in testcases: + it = pickle.loads(t) + self.assertEqual(list(it), [14, 16, 18]) + def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" # because the range validation step was eating the exception diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 3e05707b1cee69..f74c83522ec9de 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -766,7 +766,6 @@ PyTypeObject PyRange_Type = { typedef struct { PyObject_HEAD - long index; long start; long step; long len; @@ -775,18 +774,19 @@ typedef struct { static PyObject * rangeiter_next(rangeiterobject *r) { - if (r->index < r->len) - /* cast to unsigned to avoid possible signed overflow - in intermediate calculations. */ - return PyLong_FromLong((long)(r->start + - (unsigned long)(r->index++) * r->step)); + if (r->len > 0) { + long result = r->start; + r->start += r->step; + r->len--; + return PyLong_FromLong(result); + } return NULL; } static PyObject * rangeiter_len(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) { - return PyLong_FromLong(r->len - r->index); + return PyLong_FromLong(r->len); } PyDoc_STRVAR(length_hint_doc, @@ -813,8 +813,8 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) if (range == NULL) goto err; /* return the result */ - return Py_BuildValue("N(N)i", _PyEval_GetBuiltinId(&PyId_iter), - range, r->index); + return Py_BuildValue("N(N)O", _PyEval_GetBuiltinId(&PyId_iter), + range, Py_None); err: Py_XDECREF(start); Py_XDECREF(stop); @@ -833,7 +833,8 @@ rangeiter_setstate(rangeiterobject *r, PyObject *state) index = 0; else if (index > r->len) index = r->len; /* exhausted iterator */ - r->index = index; + r->start += index * r->step; + r->len -= index; Py_RETURN_NONE; } @@ -931,13 +932,11 @@ fast_range_iter(long start, long stop, long step) return NULL; } it->len = (long)ulen; - it->index = 0; return (PyObject *)it; } typedef struct { PyObject_HEAD - PyObject *index; PyObject *start; PyObject *step; PyObject *len; @@ -946,7 +945,8 @@ typedef struct { static PyObject * longrangeiter_len(longrangeiterobject *r, PyObject *no_args) { - return PyNumber_Subtract(r->len, r->index); + Py_INCREF(r->len); + return r->len; } static PyObject * @@ -976,7 +976,7 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) /* return the result */ return Py_BuildValue("N(N)O", _PyEval_GetBuiltinId(&PyId_iter), - range, r->index); + range, Py_None); } static PyObject * @@ -999,8 +999,18 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state) if (cmp > 0) state = r->len; } - Py_INCREF(state); - Py_XSETREF(r->index, state); + PyObject *new_len = PyNumber_Subtract(r->len, state); + if (new_len == NULL) + return NULL; + Py_SETREF(r->len, new_len); + PyObject *product = PyNumber_Multiply(state, r->step); + if (product == NULL) + return NULL; + PyObject *new_start = PyNumber_Add(r->start, product); + Py_DECREF(product); + if (new_start == NULL) + return NULL; + Py_SETREF(r->start, new_start); Py_RETURN_NONE; } @@ -1017,7 +1027,6 @@ static PyMethodDef longrangeiter_methods[] = { static void longrangeiter_dealloc(longrangeiterobject *r) { - Py_XDECREF(r->index); Py_XDECREF(r->start); Py_XDECREF(r->step); Py_XDECREF(r->len); @@ -1027,29 +1036,21 @@ longrangeiter_dealloc(longrangeiterobject *r) static PyObject * longrangeiter_next(longrangeiterobject *r) { - PyObject *product, *new_index, *result; - if (PyObject_RichCompareBool(r->index, r->len, Py_LT) != 1) + if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1) return NULL; - new_index = PyNumber_Add(r->index, _PyLong_GetOne()); - if (!new_index) + PyObject *new_start = PyNumber_Add(r->start, r->step); + if (new_start == NULL) { return NULL; - - product = PyNumber_Multiply(r->index, r->step); - if (!product) { - Py_DECREF(new_index); - return NULL; - } - - result = PyNumber_Add(r->start, product); - Py_DECREF(product); - if (result) { - Py_SETREF(r->index, new_index); } - else { - Py_DECREF(new_index); + PyObject *new_len = PyNumber_Subtract(r->len, _PyLong_GetOne()); + if (new_len == NULL) { + Py_DECREF(new_start); + return NULL; } - + PyObject *result = r->start; + r->start = new_start; + Py_SETREF(r->len, new_len); return result; } @@ -1128,11 +1129,9 @@ range_iter(PyObject *seq) it->start = r->start; it->step = r->step; it->len = r->length; - it->index = _PyLong_GetZero(); Py_INCREF(it->start); Py_INCREF(it->step); Py_INCREF(it->len); - Py_INCREF(it->index); return (PyObject *)it; } @@ -1210,7 +1209,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type); if (it == NULL) return NULL; - it->index = it->start = it->step = NULL; + it->start = it->step = NULL; /* start + (len - 1) * step */ it->len = range->length; @@ -1235,8 +1234,6 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) if (!it->step) goto create_failure; - it->index = _PyLong_GetZero(); - Py_INCREF(it->index); return (PyObject *)it; create_failure: From 1cd01387ed050ecd71fee1196bb66a93d796abdb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Fri, 27 Aug 2021 06:35:33 +0300 Subject: [PATCH 2/8] Fix sizeof test. --- Lib/test/test_sys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index dba4928ec261ac..d99190ca96b1c5 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1336,7 +1336,8 @@ def delx(self): del self.__x # PyCapsule # XXX # rangeiterator - check(iter(range(1)), size('4l')) + check(iter(range(1)), size('3l')) + check(iter(range(2**65)), size('3P')) # reverse check(reversed(''), size('nP')) # range From 3133f664f1cd7c04ae60c7ca51b6ea3224024ac9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Sun, 29 Aug 2021 15:23:51 +0300 Subject: [PATCH 3/8] Add explicit tests for __setstate__. --- Lib/test/test_range.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index c94dba799adedc..177890b0f7e728 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -450,6 +450,20 @@ def test_iterator_unpickle_compat(self): it = pickle.loads(t) self.assertEqual(list(it), [14, 16, 18]) + def test_iterator_setstate(self): + it = iter(range(10, 20, 2)) + it.__setstate__(2) + self.assertEqual(list(it), [14, 16, 18]) + it = reversed(range(10, 20, 2)) + it.__setstate__(3) + self.assertEqual(list(it), [12, 10]) + it = iter(range(-2**65, 20, 2)) + it.__setstate__(2**64 + 7) + self.assertEqual(list(it), [14, 16, 18]) + it = reversed(range(10, 2**65, 2)) + it.__setstate__(2**64 - 7) + self.assertEqual(list(it), [12, 10]) + def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" # because the range validation step was eating the exception From 7a42474da799a5b75ac661247c817771c18ee5a3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Sun, 29 Aug 2021 15:55:28 +0300 Subject: [PATCH 4/8] Add a NEWS entry. --- .../Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst new file mode 100644 index 00000000000000..481ab53e4f5197 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-29-15-55-19.bpo-45026.z7nTA3.rst @@ -0,0 +1,3 @@ +Optimize the :class:`range` object iterator. It is now smaller, faster +iteration of ranges containing large numbers. Smaller pickles, faster +unpickling. From 177da8c11e973bc6649d0c3d5ee06da54d020804 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Sun, 5 Sep 2021 14:26:29 +0300 Subject: [PATCH 5/8] Keep stop instead of len. --- Objects/rangeobject.c | 156 ++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 83 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 03e16aec4c653a..0c8d156fee37c6 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -767,17 +767,44 @@ PyTypeObject PyRange_Type = { typedef struct { PyObject_HEAD long start; + long stop; long step; - long len; } rangeiterobject; +/* Return number of items in range (lo, hi, step). step != 0 + * required. The result always fits in an unsigned long. + */ +static unsigned long +get_len_of_range(long lo, long hi, long step) +{ + /* ------------------------------------------------------------- + If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty. + Else for step > 0, if n values are in the range, the last one is + lo + (n-1)*step, which must be <= hi-1. Rearranging, + n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives + the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so + the RHS is non-negative and so truncation is the same as the + floor. Letting M be the largest positive long, the worst case + for the RHS numerator is hi=M, lo=-M-1, and then + hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough + precision to compute the RHS exactly. The analysis for step < 0 + is similar. + ---------------------------------------------------------------*/ + assert(step != 0); + if (step > 0 && lo < hi) + return 1UL + (hi - 1UL - lo) / step; + else if (step < 0 && lo > hi) + return 1UL + (lo - 1UL - hi) / (0UL - step); + else + return 0UL; +} + static PyObject * rangeiter_next(rangeiterobject *r) { - if (r->len > 0) { + if (r->step > 0 ? r->start < r->stop : r->start > r->stop) { long result = r->start; r->start += r->step; - r->len--; return PyLong_FromLong(result); } return NULL; @@ -786,7 +813,8 @@ rangeiter_next(rangeiterobject *r) static PyObject * rangeiter_len(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) { - return PyLong_FromLong(r->len); + unsigned long ulen = get_len_of_range(r->start, r->stop, r->step); + return PyLong_FromUnsignedLong(ulen); } PyDoc_STRVAR(length_hint_doc, @@ -802,7 +830,7 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) start = PyLong_FromLong(r->start); if (start == NULL) goto err; - stop = PyLong_FromLong(r->start + r->len * r->step); + stop = PyLong_FromLong(r->stop); if (stop == NULL) goto err; step = PyLong_FromLong(r->step); @@ -831,10 +859,12 @@ rangeiter_setstate(rangeiterobject *r, PyObject *state) /* silently clip the index value */ if (index < 0) index = 0; - else if (index > r->len) - index = r->len; /* exhausted iterator */ + else { + unsigned long ulen = get_len_of_range(r->start, r->stop, r->step); + if ((unsigned long)index > ulen) + index = (long)ulen; /* exhausted iterator */ + } r->start += index * r->step; - r->len -= index; Py_RETURN_NONE; } @@ -884,34 +914,6 @@ PyTypeObject PyRangeIter_Type = { 0, /* tp_members */ }; -/* Return number of items in range (lo, hi, step). step != 0 - * required. The result always fits in an unsigned long. - */ -static unsigned long -get_len_of_range(long lo, long hi, long step) -{ - /* ------------------------------------------------------------- - If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty. - Else for step > 0, if n values are in the range, the last one is - lo + (n-1)*step, which must be <= hi-1. Rearranging, - n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives - the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so - the RHS is non-negative and so truncation is the same as the - floor. Letting M be the largest positive long, the worst case - for the RHS numerator is hi=M, lo=-M-1, and then - hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough - precision to compute the RHS exactly. The analysis for step < 0 - is similar. - ---------------------------------------------------------------*/ - assert(step != 0); - if (step > 0 && lo < hi) - return 1UL + (hi - 1UL - lo) / step; - else if (step < 0 && lo > hi) - return 1UL + (lo - 1UL - hi) / (0UL - step); - else - return 0UL; -} - /* Initialize a rangeiter object. If the length of the rangeiter object is not representable as a C long, OverflowError is raised. */ @@ -922,46 +924,38 @@ fast_range_iter(long start, long stop, long step, long len) if (it == NULL) return NULL; it->start = start; + it->stop = stop; it->step = step; - it->len = len; return (PyObject *)it; } typedef struct { PyObject_HEAD PyObject *start; + PyObject *stop; PyObject *step; - PyObject *len; } longrangeiterobject; static PyObject * longrangeiter_len(longrangeiterobject *r, PyObject *no_args) { - Py_INCREF(r->len); - return r->len; + return compute_range_length(r->start, r->stop, r->step); } static PyObject * longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) { - PyObject *product, *stop=NULL; PyObject *range; - /* create a range object for pickling. Must calculate the "stop" value */ - product = PyNumber_Multiply(r->len, r->step); - if (product == NULL) - return NULL; - stop = PyNumber_Add(r->start, product); - Py_DECREF(product); - if (stop == NULL) - return NULL; + /* create a range object for pickling. */ Py_INCREF(r->start); + Py_INCREF(r->stop); Py_INCREF(r->step); range = (PyObject*)make_range_object(&PyRange_Type, - r->start, stop, r->step); + r->start, r->stop, r->step); if (range == NULL) { Py_DECREF(r->start); - Py_DECREF(stop); + Py_DECREF(r->stop); Py_DECREF(r->step); return NULL; } @@ -978,23 +972,28 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state) int cmp; /* clip the value */ - cmp = PyObject_RichCompareBool(state, zero, Py_LT); + cmp = PyObject_RichCompareBool(state, zero, Py_LE); if (cmp < 0) return NULL; if (cmp > 0) { - state = zero; + Py_RETURN_NONE; } - else { - cmp = PyObject_RichCompareBool(r->len, state, Py_LT); - if (cmp < 0) - return NULL; - if (cmp > 0) - state = r->len; + PyObject *length = compute_range_length(r->start, r->stop, r->step); + if (length == NULL) { + return NULL; } - PyObject *new_len = PyNumber_Subtract(r->len, state); - if (new_len == NULL) + cmp = PyObject_RichCompareBool(length, state, Py_LE); + if (cmp < 0) { + Py_DECREF(length); return NULL; - Py_SETREF(r->len, new_len); + } + if (cmp > 0) { + state = length; + } + else { + Py_INCREF(state); + Py_DECREF(length); + } PyObject *product = PyNumber_Multiply(state, r->step); if (product == NULL) return NULL; @@ -1020,29 +1019,24 @@ static void longrangeiter_dealloc(longrangeiterobject *r) { Py_XDECREF(r->start); + Py_XDECREF(r->stop); Py_XDECREF(r->step); - Py_XDECREF(r->len); PyObject_Free(r); } static PyObject * longrangeiter_next(longrangeiterobject *r) { - if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1) + int s = _PyLong_Sign(r->step); + if (PyObject_RichCompareBool(r->start, r->stop, s > 0 ? Py_LT : Py_GT) != 1) return NULL; PyObject *new_start = PyNumber_Add(r->start, r->step); if (new_start == NULL) { return NULL; } - PyObject *new_len = PyNumber_Subtract(r->len, _PyLong_GetOne()); - if (new_len == NULL) { - Py_DECREF(new_start); - return NULL; - } PyObject *result = r->start; r->start = new_start; - Py_SETREF(r->len, new_len); return result; } @@ -1129,11 +1123,11 @@ range_iter(PyObject *seq) return NULL; it->start = r->start; + it->stop = r->stop; it->step = r->step; - it->len = r->length; Py_INCREF(it->start); + Py_INCREF(it->stop); Py_INCREF(it->step); - Py_INCREF(it->len); return (PyObject *)it; } @@ -1142,7 +1136,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) { rangeobject *range = (rangeobject*) seq; longrangeiterobject *it; - PyObject *sum, *diff, *product; + PyObject *product; long lstart, lstop, lstep, new_start, new_stop; unsigned long ulen; @@ -1213,22 +1207,18 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) return NULL; it->start = it->step = NULL; - /* start + (len - 1) * step */ - it->len = range->length; - Py_INCREF(it->len); - - diff = PyNumber_Subtract(it->len, _PyLong_GetOne()); - if (!diff) + /* new_stop = start - step */ + it->stop = PyNumber_Subtract(range->start, range->step); + if (!it->stop) goto create_failure; - product = PyNumber_Multiply(diff, range->step); - Py_DECREF(diff); + /* new_start = new_stop + len * step */ + product = PyNumber_Multiply(range->length, range->step); if (!product) goto create_failure; - sum = PyNumber_Add(range->start, product); + it->start = PyNumber_Add(it->stop, product); Py_DECREF(product); - it->start = sum; if (!it->start) goto create_failure; From dced0e69e4b703ae9706a7084f082cef7fc46aee Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Fri, 10 Sep 2021 19:33:40 +0300 Subject: [PATCH 6/8] Fix refleaks. --- Objects/rangeobject.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 0c8d156fee37c6..29d40167dd79f2 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -990,13 +990,11 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state) if (cmp > 0) { state = length; } - else { - Py_INCREF(state); - Py_DECREF(length); - } PyObject *product = PyNumber_Multiply(state, r->step); - if (product == NULL) + Py_DECREF(length); + if (product == NULL) { return NULL; + } PyObject *new_start = PyNumber_Add(r->start, product); Py_DECREF(product); if (new_start == NULL) From 189ca23ec32e674016073c453c75c44bd54a8bd9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Sun, 27 Nov 2022 17:30:24 +0200 Subject: [PATCH 7/8] Unify code with range-iter. --- Objects/rangeobject.c | 2 +- Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 523d99ae47229a..6d98c080bd75d2 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -786,7 +786,7 @@ rangeiter_next(_PyRangeIterObject *r) { if (r->step > 0 ? r->start < r->stop : r->start > r->stop) { long result = r->start; - r->start += r->step; + r->start = result + r->step; return PyLong_FromLong(result); } return NULL; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1c1e1ad879019e..15952aad2f7c2c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2627,7 +2627,7 @@ dummy_func( } else { long value = r->start; - r->start += r->step; + r->start = value + r->step; if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { goto error; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c766dad9e25b13..e86975fbc93e3a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2645,7 +2645,7 @@ } else { long value = r->start; - r->start += r->step; + r->start = value + r->step; if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { goto error; } From d3159110639c8b0468b84d9b58955ce751681310 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <storchaka@gmail.com> Date: Tue, 29 Nov 2022 09:14:21 +0200 Subject: [PATCH 8/8] Microoptimize small range iteration. --- Objects/rangeobject.c | 6 +++--- Python/bytecodes.c | 6 +++--- Python/generated_cases.c.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 6d98c080bd75d2..fe2998a61c0a7e 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -784,9 +784,9 @@ get_len_of_range(long lo, long hi, long step) static PyObject * rangeiter_next(_PyRangeIterObject *r) { - if (r->step > 0 ? r->start < r->stop : r->start > r->stop) { - long result = r->start; - r->start = result + r->step; + long result = r->start, step = r->step; + if (step > 0 ? result < r->stop : result > r->stop) { + r->start = result + step; return PyLong_FromLong(result); } return NULL; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 15952aad2f7c2c..75cdfdbf7ba0ce 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2620,14 +2620,14 @@ dummy_func( STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); - if (r->step > 0 ? r->start >= r->stop : r->start <= r->stop) { + long value = r->start, step = r->step; + if (step > 0 ? value >= r->stop : value <= r->stop) { STACK_SHRINK(1); Py_DECREF(r); JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { - long value = r->start; - r->start = value + r->step; + r->start = value + step; if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { goto error; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e86975fbc93e3a..d92b42e5253a3a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2638,14 +2638,14 @@ STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); - if (r->step > 0 ? r->start >= r->stop : r->start <= r->stop) { + long value = r->start, step = r->step; + if (step > 0 ? value >= r->stop : value <= r->stop) { STACK_SHRINK(1); Py_DECREF(r); JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { - long value = r->start; - r->start = value + r->step; + r->start = value + step; if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { goto error; }