Skip to content

Commit 76b9c9e

Browse files
magic numbers
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
1 parent 9fbec75 commit 76b9c9e

2 files changed

Lines changed: 29 additions & 25 deletions

File tree

Include/internal/pycore_optimizer.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,22 @@ extern "C" {
4545
* Runtime charging uses trace-buffer capacity consumed for each bytecode. */
4646
#define AVG_SLOTS_PER_INSTRUCTION 6
4747

48-
/* Heuristic backward-edge penalty: leave room for about
48+
/* Heuristic backward-edge exit quality: leave room for about 1 unroll and
4949
* N_BACKWARD_SLACK more bytecodes before reaching EXIT_QUALITY_CLOSE_LOOP,
5050
* based on AVG_SLOTS_PER_INSTRUCTION. */
5151
#define N_BACKWARD_SLACK 50
52-
#define FITNESS_BACKWARD_EDGE (FITNESS_INITIAL/2 \
53-
- N_BACKWARD_SLACK * AVG_SLOTS_PER_INSTRUCTION)
52+
#define EXIT_QUALITY_BACKWARD_EDGE (EXIT_QUALITY_CLOSE_LOOP / 2 - N_BACKWARD_SLACK * AVG_SLOTS_PER_INSTRUCTION)
5453

5554
/* Backward edge penalty for JUMP_BACKWARD_NO_INTERRUPT (coroutines/yield-from).
56-
* Smaller than FITNESS_BACKWARD_EDGE since these loops are very short. */
57-
#define FITNESS_BACKWARD_EDGE_COROUTINE (FITNESS_BACKWARD_EDGE / 4)
55+
* Smaller than FITNESS_BACKWARD_EDGE since we want to trace through them. */
56+
#define EXIT_QUALITY_BACKWARD_EDGE_COROUTINE (EXIT_QUALITY_BACKWARD_EDGE / 8)
5857

5958
/* Penalty for a perfectly balanced (50/50) branch.
60-
* 7 such branches (after per-slot cost) exhaust fitness to EXIT_QUALITY_DEFAULT. */
61-
#define FITNESS_BRANCH_BALANCED ((FITNESS_INITIAL - EXIT_QUALITY_DEFAULT) / (7 * AVG_SLOTS_PER_INSTRUCTION))
59+
* 7 such branches (after per-slot cost) exhaust fitness to EXIT_QUALITY_DEFAULT.
60+
* The calculation assumes the branches are spread out roughly equally throughout the trace.
61+
*/
62+
#define FITNESS_BRANCH_BALANCED ((FITNESS_INITIAL - EXIT_QUALITY_DEFAULT - \
63+
(MAX_TARGET_LENGTH / 7 * AVG_SLOTS_PER_INSTRUCTION)) / (7))
6264

6365

6466
typedef struct _PyJitUopBuffer {

Python/optimizer.c

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -614,15 +614,25 @@ static inline int32_t
614614
compute_exit_quality(_Py_CODEUNIT *target_instr, int opcode,
615615
const _PyJitTracerState *tracer)
616616
{
617-
if (target_instr == tracer->initial_state.start_instr ||
618-
target_instr == tracer->initial_state.close_loop_instr) {
619-
return EXIT_QUALITY_CLOSE_LOOP;
620-
}
621-
if (target_instr->op.code == ENTER_EXECUTOR && !_PyJit_EnterExecutorShouldStopTracing(opcode)) {
622-
return EXIT_QUALITY_ENTER_EXECUTOR;
623-
}
624-
else if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] > 0) {
625-
return EXIT_QUALITY_SPECIALIZABLE;
617+
// We need to check for this, otherwise the first instruction (JUMP_BACKWARD usually)
618+
// is mistakenly thought of as an exit.
619+
if (uop_buffer_length((_PyJitUopBuffer *)&tracer->code_buffer) > CODE_SIZE_NO_PROGRESS) {
620+
if (target_instr == tracer->initial_state.start_instr ||
621+
target_instr == tracer->initial_state.close_loop_instr) {
622+
return EXIT_QUALITY_CLOSE_LOOP;
623+
}
624+
else if (target_instr->op.code == ENTER_EXECUTOR && !_PyJit_EnterExecutorShouldStopTracing(opcode)) {
625+
return EXIT_QUALITY_ENTER_EXECUTOR;
626+
}
627+
else if (opcode == JUMP_BACKWARD_JIT || opcode == JUMP_BACKWARD) {
628+
return EXIT_QUALITY_BACKWARD_EDGE;
629+
}
630+
else if (opcode == JUMP_BACKWARD_NO_INTERRUPT) {
631+
return EXIT_QUALITY_BACKWARD_EDGE_COROUTINE;
632+
}
633+
else if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] > 0) {
634+
return EXIT_QUALITY_SPECIALIZABLE;
635+
}
626636
}
627637
return EXIT_QUALITY_DEFAULT;
628638
}
@@ -820,8 +830,6 @@ _PyJit_translate_single_bytecode_to_trace(
820830
// One for possible _DEOPT, one because _CHECK_VALIDITY itself might _DEOPT
821831
trace->end -= 2;
822832

823-
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];
824-
825833
assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG);
826834
assert(!_PyErr_Occurred(tstate));
827835

@@ -843,6 +851,7 @@ _PyJit_translate_single_bytecode_to_trace(
843851
trace->end -= needs_guard_ip;
844852

845853
#if Py_DEBUG
854+
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];
846855
int space_needed = expansion->nuops + needs_guard_ip + 2 + (!OPCODE_HAS_NO_SAVE_IP(opcode));
847856
assert(uop_buffer_remaining_space(trace) > space_needed);
848857
#endif
@@ -880,15 +889,8 @@ _PyJit_translate_single_bytecode_to_trace(
880889
case JUMP_BACKWARD_NO_JIT:
881890
case JUMP_BACKWARD:
882891
ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target);
883-
tracer->translator_state.fitness -= FITNESS_BACKWARD_EDGE;
884-
DPRINTF(3, " backward edge penalty: -%d -> fitness=%d\n",
885-
FITNESS_BACKWARD_EDGE, tracer->translator_state.fitness);
886892
break;
887893
case JUMP_BACKWARD_NO_INTERRUPT:
888-
tracer->translator_state.fitness -= FITNESS_BACKWARD_EDGE_COROUTINE;
889-
DPRINTF(3, " coroutine backward edge penalty: -%d -> fitness=%d\n",
890-
FITNESS_BACKWARD_EDGE_COROUTINE,
891-
tracer->translator_state.fitness);
892894
break;
893895

894896
case RESUME:

0 commit comments

Comments
 (0)