Repository navigation
gh-91432: Specialize FOR_ITER #91713
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
3a7f8df
3b5ce1e
b21b5f4
37269cf
ea0a7ee
0751228
dc80fda
e429410
1cb2de7
73fa01c
4213582
bf58358
bd7575e
db8754b
2050c4e
824e966
6b16772
c2a75a5
6696384
d297091
22635a6
f360d65
81e0500
c2eab68
25689c3
b5df047
2b1c170
91d280c
eba5e60
5b153d7
76f9a74
0565a68
9ba5b79
6cdf0e4
03dde6a
f1e2d39
463c3b9
ed29777
188b357
36d0999
ad2e969
d55868f
2d6ee26
db21da1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4399,17 +4399,42 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |
| _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); | ||
| DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); | ||
| STAT_INC(FOR_ITER, hit); | ||
| if (r->index < r->len) { | ||
| PyObject *res = PyLong_FromLong( | ||
| (long)(r->start + (unsigned long)(r->index++) * r->step)); | ||
| if (res == NULL) { | ||
| goto error; | ||
| _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; | ||
| assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); | ||
| PyObject **local_ptr = &GETLOCAL(_Py_OPARG(next)); | ||
| PyObject *local = *local_ptr; | ||
| if (r->index >= r->len) { | ||
| goto iterator_exhausted_no_error; | ||
| } | ||
| JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); | ||
| sdigit value = r->start + (digit)(r->index++) * r->step; | ||
| if (value < _PY_NSMALLPOSINTS && value >= -_PY_NSMALLNEGINTS) { | ||
|
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 think there are too many branches here. This also includes far too much knowledge of the internals of the Would performance be measurably worse if
Member
Author
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 think there may be a middle ground here: add a function like this to longobject.c: Sort of similar in spirit to That way, the PyLongObject implementation details stay out of the eval loop, but we still get the benefit of avoiding the allocation, which I believe is the main source of the speedup.
Member
Author
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. This could theoretically be re-used for And even if the implementation of integers changes (e.g. new freelists, no more small ints), the operation still makes sense. |
||
| *local_ptr = Py_NewRef(&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS+value]); | ||
| Py_XDECREF(local); | ||
| NOTRACE_DISPATCH(); | ||
| } | ||
|
sweeneyde marked this conversation as resolved.
Outdated
|
||
| if (local && PyLong_CheckExact(local) && Py_REFCNT(local) == 1) { | ||
| if (value > 0) { | ||
| assert(value <= PyLong_MASK); | ||
| ((PyLongObject *)local)->ob_digit[0] = value; | ||
| Py_SET_SIZE(local, 1); | ||
| } | ||
| else { | ||
| assert(value >= -(sdigit)PyLong_MASK); | ||
| ((PyLongObject *)local)->ob_digit[0] = -(sdigit)value; | ||
| Py_SET_SIZE(local, -1); | ||
| } | ||
| PUSH(res); | ||
| JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); | ||
| NOTRACE_DISPATCH(); | ||
| } | ||
| goto iterator_exhausted_no_error; | ||
| PyObject *res = PyLong_FromLong(value); | ||
| if (res == NULL) { | ||
| // undo the JUMPBY | ||
| next_instr -= INLINE_CACHE_ENTRIES_FOR_ITER + 1; | ||
| goto error; | ||
| } | ||
| *local_ptr = res; | ||
| Py_XDECREF(local); | ||
| NOTRACE_DISPATCH(); | ||
| } | ||
|
|
||
| TARGET(BEFORE_ASYNC_WITH) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be cleaner to change the signature of
fast_range_iterto take sdigits, rather than longs?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not so sure -- downcasts need to happen somewhere, and
fast_range_iteris called in two places. ThePyLong_AsLongcalls could theoretically be replaced withPy_SIZEchecks andob_digit[0]accesses, but I think the existing code is fine using the public API.