Skip to content

gh-146073: Add fitness/exit quality mechanism for JIT trace frontend#148089

Open
cocolato wants to merge 31 commits intopython:mainfrom
cocolato:jit-tracer-fitness
Open

gh-146073: Add fitness/exit quality mechanism for JIT trace frontend#148089
cocolato wants to merge 31 commits intopython:mainfrom
cocolato:jit-tracer-fitness

Conversation

@cocolato
Copy link
Copy Markdown
Member

@cocolato cocolato commented Apr 4, 2026

@cocolato

This comment was marked as outdated.

@cocolato
Copy link
Copy Markdown
Member Author

cocolato commented Apr 6, 2026

It appears that the current parameters do not yet guarantee runtime safety; I will continue to work on fixes and optimizations.

@markshannon
Copy link
Copy Markdown
Member

I've commented on the issue #146073 (comment)

@cocolato

This comment was marked as outdated.

@cocolato
Copy link
Copy Markdown
Member Author

@markshannon @Fidget-Spinner gentle ping, I’d like to hear your suggestions about the current parameters

Copy link
Copy Markdown
Member

@markshannon markshannon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've a few comments, mostly broad ideas and suggestions, rather than anything that needs fixing.

I think we should merge this soon. We can tweak the parameters later as we refine our ideas.


/* Exit quality thresholds: trace stops when fitness < exit_quality.
* Higher = trace is more willing to stop here. */
#define EXIT_QUALITY_CLOSE_LOOP (FITNESS_INITIAL / 2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want this higher, close to FITNESS_INITIAL*0.9. It is only super-short loops that we want to unroll.

* Higher = trace is more willing to stop here. */
#define EXIT_QUALITY_CLOSE_LOOP (FITNESS_INITIAL / 2)
#define EXIT_QUALITY_ENTER_EXECUTOR (FITNESS_INITIAL * 3 / 8)
#define EXIT_QUALITY_DEFAULT (FITNESS_INITIAL / 8)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd increase this to make sure that the fitness cannot drop from above EXIT_QUALITY_DEFAULT to below EXIT_QUALITY_SPECIALIZABLE in a single uop.

* N_BACKWARD_SLACK more bytecodes before reaching EXIT_QUALITY_CLOSE_LOOP,
* based on AVG_SLOTS_PER_INSTRUCTION. */
#define N_BACKWARD_SLACK 50
#define FITNESS_BACKWARD_EDGE (FITNESS_INITIAL - EXIT_QUALITY_CLOSE_LOOP \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A backwards edge isn't necessarily bad, although too many suggests a poor trace.

Maybe don't penalize the first backwards edge much, but penalize subsequent ones a lot.
What we really want is to avoid unrolling a loop that doesn't include the trace start.

It would be easier to reason about, if we used a simpler calculation for the value.


/* Backward edge penalty for JUMP_BACKWARD_NO_INTERRUPT (coroutines/yield-from).
* Smaller than FITNESS_BACKWARD_EDGE since these loops are very short. */
#define FITNESS_BACKWARD_EDGE_COROUTINE (FITNESS_BACKWARD_EDGE / 4)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not the length of the loop that matter. We want to include yields in generators in the traces of the loop that calls them, whether that is a yield from loop or a for loop doesn't much matter.

assert(curr_instr->op.code == JUMP_BACKWARD_JIT || curr_instr->op.code == RESUME_CHECK_JIT || (exit != NULL));
tracer->initial_state.jump_backward_instr = curr_instr;

// Reduce side-trace fitness as chain depth grows, but clamp the reduction
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably fine for now, but reducing the fitness can skew some of our assumptions about back edges and such.

One other thing to note for the future, is that the fitness at an exit might help us pick a better warmup value for that exit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark is right, we should remove this feature. Side exits are important and we shouldn't penalize them.

@cocolato
Copy link
Copy Markdown
Member Author

cocolato commented Apr 14, 2026

@markshannon Thanks for review! I'm holding off on changing the fitness parameters for now, but I can run some benchmarks if you think it's necessary.

@Fidget-Spinner
Copy link
Copy Markdown
Member

Fidget-Spinner commented Apr 14, 2026

Still seeing a big slowdown in richards on https://github.com/colesbury/fastmark:

Main of this branch:

Benchmark                     Time      Useful Work
richards                      115.9 ms      (100%)
richards_super                103.5 ms      (100%)

This branch:

Benchmark                     Time      Useful Work
richards                      119.2 ms      (100%)
richards_super                118.5 ms      (100%)

I'm going to check if this is affecting the optimizer somehow.

Treat back edges as an exit, not a penalty, this way they are more likely to end at a backedge instead of ending at random spots
OPT_STAT_INC(trace_too_long);
goto done;
}
assert(uop_buffer_remaining_space(trace) > space_needed);
Copy link
Copy Markdown
Member Author

@cocolato cocolato Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAX_TARGET_LENGTH must be less than UOP_MAX_TRACE_LENGTH / OPTIMIZER_EFFECTIVENESS; otherwise, assert(uop_buffer_remaining_space(trace) > space_needed) will fail. Or should we revert the assert to a check?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to keep it as an assert.
Otherwise we need to handle an awkward failure mode.

@markshannon
Copy link
Copy Markdown
Member

Increasing the max trace length is only going to help if the trace is stopping too early.
How big are the traces on main for richards?

@cocolato
Copy link
Copy Markdown
Member Author

Increasing the max trace length is only going to help if the trace is stopping too early. How big are the traces on main for richards?

The main branch executors dump by sys._dump_tracelets('richards.gvz'):
graphviz (1)

@cocolato
Copy link
Copy Markdown
Member Author

After this pr:
graphviz (2)

@Fidget-Spinner
Copy link
Copy Markdown
Member

Fidget-Spinner commented Apr 14, 2026

I think we can safely reduce the max trace length, let me do that.

There were two problems with the older code:

  1. Branch penalty was treated as before instruction count, when it should be treated as the sum over the expected trace length.
  2. Treating non-closing JUMP_BACKWARD as penalty rather than exit quality seems to make it such that we stop traces at a certain offset after seeing a JUMP_BACKWARD. This isn't what we want. instead, we want to treat it as an exit that is worth stopping at (to increase the likelihood of linking to another trace).

New code has almost no slowdown on Richards, and a huge speedup on telco benchmark.

Benchmark                     Time      Useful Work
richards                      113.4 ms      (100%)
deltablue                     196.4 ms      (100%)
raytrace                      267.3 ms      (100%)
nbody                         150.6 ms      (100%)
go                            111.5 ms      (100%)
telco                        3517.8 ms      (100%)

Main:

Benchmark                     Time      Useful Work
richards                      111.6 ms      (100%)
deltablue                     192.2 ms      (100%)
raytrace                      270.4 ms      (100%)
nbody                         151.1 ms      (100%)
go                            112.6 ms      (100%)
telco                        3809.7 ms      (100%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants