-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
NOTRACE_DISPATCH
breaks tracing
#96636
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
We can keep I agree, the other uses are just too risky. We can fix this more efficiently for 3.12 using PEP 669 (or a similar approach internally if the PEP is rejected). |
Thinking about this a bit more, the
which has the same problem. |
Hm, I don't see how, since Regardless, I think that the fact that we're having trouble proving the correctness of even simple instructions like these is a strong point in favor of just using |
Co-authored-by: Brandt Bucher <[email protected]>
Co-authored-by: Brandt Bucher <[email protected]> (cherry picked from commit aa3b4cf)
A question about the removal of |
I don't think so, since |
Okay I see. I was thinking that something like
So I believe it impossible for an adaptive instruction to be followed by |
In practice, yeah, it doesn't really happen, since the specialization code needs to be extremely careful not to change the state of the program it's currently trying to optimize. But it's not impossible, and catching it is really tricky (as we've seen). For example, the |
There seem to be only three people who understand DO_TRACING. I can't find any docs for it, not even a single comment explaining it. Maybe we should have a write-up somewhere? I would volunteer but I don't think I understand enough of it. So far I believe this: when tracing is turned on (in quickened code only?) the opcode variable (but not the opcode byte in the bytecode string) is OR-ed with 255, and that makes us hit the |
The path is the same for both quickened and unquickened code.
Yep. This or-ing is done to avoid branching on the tracing flag, and just using the normal instruction dispatch mechanism instead with a branchless modification. So, to give a concrete example, let's assume that I have the quickened instructions
So you can really think of tracing as executing two opcode cases per instruction: first
Those are for more "predictable" tracing events like |
Thanks! Let’s put that in a file and check it in. |
Co-authored-by: Brandt Bucher <[email protected]> (cherry picked from commit aa3b4cf)
Most of our quickened instructions start by asserting that tracing isn't enabled, and end by calling
NOTRACE_DISPATCH()
, which skips tracing checks. This is safe for many of our specializations (like those forBINARY_OP
andCOMPARE_OP
, which operate on known safe types), but not all of them. The problem is that any time we decref an unknown object, arbitrary finalizers could run and enable tracing.It seems that our current practice is predicated on the idea that it's okay for tracing to start "sometime in the future", since we make no guarantees about when finalizers run. There are two issues with this:
Here's a sort-of-minimal reproducer of what this can look like on debug builds:
In this case
LOAD_ATTR_INSTANCE_VALUE
'sNOTRACE_DISPATCH()
invalidatesLOAD_GLOBAL_MODULE
'sassert(cframe.use_tracing == 0)
.One option is to just rip out the asserts, which solves the more serious issue of crashing debug builds. However, I think that the best solution may be to stop using
NOTRACE_DISPATCH()
altogether. It's really hard to get right, and only saves us a single memory load and bitwise|
. I benchmarked a branch that usesDISPATCH()
for all instructions, and the result was "1.00x slower" thanmain
. @markshannon tried the same thing, and got "1.01x slower". So going this route isn't free, but also not a huge penalty for correctness and peace-of-mind.Flagging as a release blocker, since 3.11 is affected.
@markshannon @sweeneyde @pablogsal
The text was updated successfully, but these errors were encountered: