From 2fa0b0d756ec0ad5bd66be48dcf6411e879ffb73 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka 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 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 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 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 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 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 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 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; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy