Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
gh-89189: More compact range iterator#27986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
2fa0b0d1cd0138214f2043133f667a42474175b0d322f76b820e3149abf29710ca67c210822c0050df74File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,7 +10,6 @@ extern "C" { | ||
| typedef struct { | ||
| PyObject_HEAD | ||
| long start; | ||
| long step; | ||
| long len; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -756,18 +756,19 @@ PyTypeObject PyRange_Type = { | ||
| static PyObject * | ||
| rangeiter_next(_PyRangeIterObject *r) | ||
| { | ||
| if (r->len > 0) { | ||
| long result = r->start; | ||
| r->start = result + r->step; | ||
| r->len--; | ||
| return PyLong_FromLong(result); | ||
| } | ||
| return NULL; | ||
| } | ||
| static PyObject * | ||
| rangeiter_len(_PyRangeIterObject *r, PyObject *Py_UNUSED(ignored)) | ||
| { | ||
| return PyLong_FromLong(r->len); | ||
| } | ||
| PyDoc_STRVAR(length_hint_doc, | ||
| @@ -794,8 +795,8 @@ rangeiter_reduce(_PyRangeIterObject *r, PyObject *Py_UNUSED(ignored)) | ||
| if (range == NULL) | ||
| goto err; | ||
| /* return the result */ | ||
| return Py_BuildValue("N(N)O", _PyEval_GetBuiltin(&_Py_ID(iter)), | ||
| range,Py_None); | ||
Comment on lines +798 to +799 Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I'm not sure we care, but we might -- am I right that this writes pickles that can't be read by 3.11 or before? MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. No, absolutely no. I would never propose such change. Internals will be different, but the unpickled iterator wil produce the same values. | ||
| err: | ||
| Py_XDECREF(start); | ||
| Py_XDECREF(stop); | ||
| @@ -814,7 +815,8 @@ rangeiter_setstate(_PyRangeIterObject *r, PyObject *state) | ||
| index = 0; | ||
| else if (index > r->len) | ||
| index = r->len; /* exhausted iterator */ | ||
| r->start += index * r->step; | ||
| r->len -= index; | ||
| Py_RETURN_NONE; | ||
| } | ||
| @@ -904,13 +906,11 @@ fast_range_iter(long start, long stop, long step, long len) | ||
| it->start = start; | ||
| it->step = step; | ||
| it->len = len; | ||
| return (PyObject *)it; | ||
| } | ||
| typedef struct { | ||
| PyObject_HEAD | ||
| PyObject *start; | ||
| PyObject *step; | ||
| PyObject *len; | ||
| @@ -919,7 +919,8 @@ typedef struct { | ||
| static PyObject * | ||
| longrangeiter_len(longrangeiterobject *r, PyObject *no_args) | ||
| { | ||
| Py_INCREF(r->len); | ||
| return r->len; | ||
| } | ||
| static PyObject * | ||
| @@ -946,8 +947,8 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) | ||
| } | ||
| /* return the result */ | ||
| return Py_BuildValue("N(N)O", _PyEval_GetBuiltin(&_Py_ID(iter)), | ||
| range,Py_None); | ||
| } | ||
| static PyObject * | ||
| @@ -970,7 +971,22 @@ longrangeiter_setstate(longrangeiterobject *r, PyObject *state) | ||
| if (cmp > 0) | ||
| state = r->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; | ||
| PyObject *new_len = PyNumber_Subtract(r->len, state); | ||
| if (new_len == NULL) { | ||
| Py_DECREF(new_start); | ||
| return NULL; | ||
| } | ||
| PyObject *tmp = r->start; | ||
| r->start = new_start; | ||
| Py_SETREF(r->len, new_len); | ||
| Py_DECREF(tmp); | ||
Comment on lines +986 to +989 Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I don't see a scenario where we can't just use (which would be a little easier to follow). Both I'll leave it up to you though. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Py_SETREF can only help when you assign a single object attribute, or if the attributes are independent. Two sequential We do not check the type of It never occurs in normal code ( Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Aha, that's a scenario I hadn't considered. Maybe the next time I am asked in some kind of Q&A or interview "do you have any regrets" I should mention | ||
| Py_RETURN_NONE; | ||
| } | ||
| @@ -987,7 +1003,6 @@ static PyMethodDef longrangeiter_methods[] = { | ||
| static void | ||
| longrangeiter_dealloc(longrangeiterobject *r) | ||
| { | ||
| Py_XDECREF(r->start); | ||
| Py_XDECREF(r->step); | ||
| Py_XDECREF(r->len); | ||
| @@ -997,29 +1012,21 @@ longrangeiter_dealloc(longrangeiterobject *r) | ||
| static PyObject * | ||
| longrangeiter_next(longrangeiterobject *r) | ||
| { | ||
| if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), 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; | ||
| } | ||
| @@ -1108,7 +1115,6 @@ range_iter(PyObject *seq) | ||
| it->start = Py_NewRef(r->start); | ||
| it->step = Py_NewRef(r->step); | ||
| it->len = Py_NewRef(r->length); | ||
| return (PyObject *)it; | ||
| } | ||
| @@ -1186,7 +1192,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) | ||
| it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type); | ||
| if (it == NULL) | ||
| return NULL; | ||
| it->start = it->step = NULL; | ||
| /* start + (len - 1) * step */ | ||
| it->len = Py_NewRef(range->length); | ||
| @@ -1210,7 +1216,6 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) | ||
| if (!it->step) | ||
| goto create_failure; | ||
| return (PyObject *)it; | ||
| create_failure: | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.