-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
Exceptions slow in 3.11, depending on location #109181
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
Comments
They use an O(n) implementation internally. |
Just for the sake of completeness, the length of unreachable code without a raise statement does not influence the computation time. Benchmark script
By comparing the performance record between the two we can see that Addr2Line is the culprit And indeed, when commenting out the corresponding loops I see a reduction in the runtime Same test script as initally provided, now with commented out loop
vs
More specifically, it seems most time is spent looking for 0 bytes here. This is about where my current understanding of what this code even does ends :) |
@AlexWaygood I am also experiencing this slowdown in python 3.10.12 (since you added 3.11, 3.12 and 3.13) |
Python 3.10 is old enough that it is now only accepting bugfixes if they relate to security issues, and this isn't a security issue. For us, therefore, this is only an issue for Python 3.11+, even if it can be reproduced on Python 3.10. |
@pablogsal and thoughts on this? |
I am currently on sick leave so I cannot look at this in detail but I feel this is the balance between the compression to save disk size and the speed to "uncompress" the line table. On the other hand maybe we could defer the computation of the line table offsets only when exceptions buble up to top level, but it may be too late at that point so we may need to save extra information and maybe even the code object if is not reachable already in all cases. In any case, I don't think this is a bug. It may qualify as a regression if we all agree but I am not even sure if I would call it that, since is just a consequence of the feature. |
I agree it's not a bug. Extra cost for new features improving error messages is fine. Someone may want a switch to disable this feature, but I would argue that you should rather keep the |
I think we can make that work with |
… the line number Signed-off-by: Pablo Galindo <[email protected]>
…ine number (#111548) Signed-off-by: Pablo Galindo <[email protected]>
…compute the line number (pythonGH-111548) Signed-off-by: Pablo Galindo <[email protected]>. (cherry picked from commit abb1542) Co-authored-by: Pablo Galindo Salgado <[email protected]>
Backporting to 3.12 and 3.11 as technically this is a regression |
…compute the line number (pythonGH-111548) Signed-off-by: Pablo Galindo <[email protected]>. (cherry picked from commit abb1542) Co-authored-by: Pablo Galindo Salgado <[email protected]>
…compute the line number (pythonGH-111548) Signed-off-by: Pablo Galindo <[email protected]>. (cherry picked from commit abb1542) Co-authored-by: Pablo Galindo Salgado <[email protected]> Signed-off-by: Pablo Galindo <[email protected]>
…compute the line number (pythonGH-111548) Signed-off-by: Pablo Galindo <[email protected]>. (cherry picked from commit abb1542) Co-authored-by: Pablo Galindo Salgado <[email protected]> Signed-off-by: Pablo Galindo <[email protected]>
…lazily compute the line number (pythonGH-111548)
… the line number (python#111548) Signed-off-by: Pablo Galindo <[email protected]>
These changes introduced references leaks in 3.11 and 3.12 branches: see issue gh-111947. |
PyFrame_GetCode() returns a strong reference.
PyFrame_GetCode() returns a strong reference. (cherry picked from commit 4b0c875) Co-authored-by: Victor Stinner <[email protected]>
…111951) [3.12] gh-109181: Fix refleak in tb_get_lineno() (GH-111948) PyFrame_GetCode() returns a strong reference. (cherry picked from commit 4b0c875) Co-authored-by: Victor Stinner <[email protected]>
… the line number (python#111548) Signed-off-by: Pablo Galindo <[email protected]>
Fix for python/cpython#109181 introduced lazily computed lineno for traceback object in 3.11.7 and 3.12.1. Tested in a number of Python versions, the change seems to be safe.
Fix for python/cpython#109181 introduced lazily computed lineno for traceback object in 3.11.7 and 3.12.1. Tested in a number of Python versions, the change seems to be safe.
Fix for python/cpython#109181 introduced lazily computed lineno for traceback object in 3.11.7 and 3.12.1. Tested in a number of Python versions, the change seems to be safe.
… the line number (python#111548) Signed-off-by: Pablo Galindo <[email protected]>
Bug report
Bug description:
(From Discourse)
Consider these two functions:
The only difference is that
long()
has 100 unreached statements instead of just one. But it takes much longer in Python 3.11 (and a bit longer in Python 3.10). Times from @jamestwebber (here):Why? Shouldn't it just jump over them all and be just as fast as
short()
?Benchmark script
Attempt This Online!):
In fact it takes time linear in how many unreached statements there are. Times for 100 to 100000 unreached statements (on one line, before the
try
):Benchmark script
Attempt This Online!
The slowness happens when the unreached statements are anywhere before the
raise
, and not when they're anywhere after theraise
(demo). So it seems what matters is location of theraise
in the function. Long code before it somehow makes it slow.This has a noticeable impact on real code I wrote (assuming I pinpointed the issue correctly): two solutions for a task, and one was oddly slower (~760 vs ~660 ns) despite executing the exact same sequence of bytecode operations. Just one jump length differed, leading to a
raise
at a larger address.Benchmark script with those two solutions and the relevant test case:
The functions shall return the one item from the iterable, or raise an exception if there are fewer or more than one. Testing with an empty iterable, both get the iterator, iterate it (nothing, since it's empty), then raise. The relevant difference appears to be that the slower one has the
raise
written at the bottom, whereas the faster one has it near the top.Sample times:
Code:
Attempt This Online!
CPython versions tested on:
3.10, 3.11
Operating systems tested on:
Linux, macOS
Linked PRs
The text was updated successfully, but these errors were encountered: