Skip to content

Commit 328da67

Browse files
gh-146073: Revert "gh-146073: Add fitness/exit quality mechanism for JIT trace frontend (GH-147966)" (#148082)
This reverts commit 198b04b.
1 parent 611d606 commit 328da67

File tree

6 files changed

+7
-165
lines changed

6 files changed

+7
-165
lines changed

Include/cpython/pystats.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ typedef struct _optimization_stats {
144144
uint64_t unknown_callee;
145145
uint64_t trace_immediately_deopts;
146146
uint64_t executors_invalidated;
147-
uint64_t fitness_terminated_traces;
148147
UOpStats opcode[PYSTATS_MAX_UOP_ID + 1];
149148
uint64_t unsupported_opcode[256];
150149
uint64_t trace_length_hist[_Py_UOP_HIST_SIZE];

Include/internal/pycore_interp_structs.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,6 @@ typedef struct _PyOptimizationConfig {
449449
uint16_t side_exit_initial_value;
450450
uint16_t side_exit_initial_backoff;
451451

452-
// Trace fitness thresholds
453-
uint16_t fitness_initial;
454-
uint16_t fitness_initial_side;
455-
456452
// Optimization flags
457453
bool specialization_enabled;
458454
bool uops_optimize_enabled;

Include/internal/pycore_optimizer.h

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,6 @@ extern "C" {
1515
#include "pycore_optimizer_types.h"
1616
#include <stdbool.h>
1717

18-
/* Default fitness configuration values for trace quality control.
19-
* FITNESS_INITIAL and FITNESS_INITIAL_SIDE can be overridden via
20-
* PYTHON_JIT_FITNESS_INITIAL and PYTHON_JIT_FITNESS_INITIAL_SIDE */
21-
#define FITNESS_PER_INSTRUCTION 2
22-
#define FITNESS_BRANCH_BASE 5
23-
#define FITNESS_INITIAL (FITNESS_PER_INSTRUCTION * 1000)
24-
#define FITNESS_INITIAL_SIDE (FITNESS_INITIAL / 2)
25-
#define FITNESS_BACKWARD_EDGE (FITNESS_INITIAL / 10)
26-
27-
/* Exit quality constants for fitness-based trace termination.
28-
* Higher values mean better places to stop the trace. */
29-
30-
#define EXIT_QUALITY_DEFAULT 200
31-
#define EXIT_QUALITY_CLOSE_LOOP (4 * EXIT_QUALITY_DEFAULT)
32-
#define EXIT_QUALITY_ENTER_EXECUTOR (2 * EXIT_QUALITY_DEFAULT + 100)
33-
#define EXIT_QUALITY_SPECIALIZABLE (EXIT_QUALITY_DEFAULT / 4)
34-
3518

3619
typedef struct _PyJitUopBuffer {
3720
_PyUOpInstruction *start;
@@ -118,8 +101,7 @@ typedef struct _PyJitTracerPreviousState {
118101
} _PyJitTracerPreviousState;
119102

120103
typedef struct _PyJitTracerTranslatorState {
121-
int32_t fitness; // Current trace fitness, starts high, decrements
122-
int frame_depth; // Current inline depth (0 = root frame)
104+
int jump_backward_seen;
123105
} _PyJitTracerTranslatorState;
124106

125107
typedef struct _PyJitTracerState {

Python/optimizer.c

Lines changed: 6 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ dynamic_exit_uop[MAX_UOP_ID + 1] = {
549549
};
550550

551551

552+
#define CONFIDENCE_RANGE 1000
553+
#define CONFIDENCE_CUTOFF 333
552554

553555
#ifdef Py_DEBUG
554556
#define DPRINTF(level, ...) \
@@ -596,46 +598,6 @@ add_to_trace(
596598
((uint32_t)((INSTR) - ((_Py_CODEUNIT *)(CODE)->co_code_adaptive)))
597599

598600

599-
/* Compute branch fitness penalty based on how likely the traced path is.
600-
* The penalty is small when the traced path is common, large when rare.
601-
* A branch that historically goes the other way gets a heavy penalty. */
602-
static inline int
603-
compute_branch_penalty(uint16_t history, bool branch_taken)
604-
{
605-
int taken_count = _Py_popcount32((uint32_t)history);
606-
int on_trace_count = branch_taken ? taken_count : 16 - taken_count;
607-
int off_trace = 16 - on_trace_count;
608-
/* Linear scaling: off_trace ranges from 0 (fully biased our way)
609-
* to 16 (fully biased against us), so the penalty ranges from
610-
* FITNESS_BRANCH_BASE to FITNESS_BRANCH_BASE + 32. */
611-
return FITNESS_BRANCH_BASE + off_trace * 2;
612-
}
613-
614-
/* Compute exit quality for the current trace position.
615-
* Higher values mean better places to stop the trace. */
616-
static inline int32_t
617-
compute_exit_quality(_Py_CODEUNIT *target_instr, int opcode,
618-
const _PyJitTracerState *tracer)
619-
{
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-
if (target_instr->op.code == ENTER_EXECUTOR) {
625-
return EXIT_QUALITY_ENTER_EXECUTOR;
626-
}
627-
if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] > 0) {
628-
return EXIT_QUALITY_SPECIALIZABLE;
629-
}
630-
return EXIT_QUALITY_DEFAULT;
631-
}
632-
633-
static inline int32_t
634-
compute_frame_penalty(const _PyOptimizationConfig *cfg)
635-
{
636-
return (int32_t)cfg->fitness_initial / 10 + 1;
637-
}
638-
639601
static int
640602
is_terminator(const _PyUOpInstruction *uop)
641603
{
@@ -675,7 +637,6 @@ _PyJit_translate_single_bytecode_to_trace(
675637
_Py_CODEUNIT *this_instr = tracer->prev_state.instr;
676638
_Py_CODEUNIT *target_instr = this_instr;
677639
uint32_t target = 0;
678-
int end_trace_opcode = _DEOPT;
679640

680641
target = Py_IsNone((PyObject *)old_code)
681642
? (uint32_t)(target_instr - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR)
@@ -773,14 +734,16 @@ _PyJit_translate_single_bytecode_to_trace(
773734
DPRINTF(2, "Unsupported: oparg too large\n");
774735
unsupported:
775736
{
737+
// Rewind to previous instruction and replace with _EXIT_TRACE.
776738
_PyUOpInstruction *curr = uop_buffer_last(trace);
777739
while (curr->opcode != _SET_IP && uop_buffer_length(trace) > 2) {
778740
trace->next--;
779741
curr = uop_buffer_last(trace);
780742
}
743+
assert(curr->opcode == _SET_IP || uop_buffer_length(trace) == 2);
781744
if (curr->opcode == _SET_IP) {
782745
int32_t old_target = (int32_t)uop_get_target(curr);
783-
curr->opcode = end_trace_opcode;
746+
curr->opcode = _DEOPT;
784747
curr->format = UOP_FORMAT_TARGET;
785748
curr->target = old_target;
786749
}
@@ -800,23 +763,6 @@ _PyJit_translate_single_bytecode_to_trace(
800763
return 1;
801764
}
802765

803-
// Fitness-based trace quality check (before reserving space for this instruction)
804-
_PyJitTracerTranslatorState *ts = &tracer->translator_state;
805-
int32_t eq = compute_exit_quality(target_instr, opcode, tracer);
806-
DPRINTF(3, "Fitness check: %s(%d) fitness=%d, exit_quality=%d, depth=%d\n",
807-
_PyOpcode_OpName[opcode], oparg, ts->fitness, eq, ts->frame_depth);
808-
809-
// Check if fitness is depleted — should we stop the trace?
810-
if (ts->fitness < eq) {
811-
// This is a tracer heuristic rather than normal program control flow,
812-
// so leave operand1 clear and let the resulting side exit increase chain_depth.
813-
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target);
814-
OPT_STAT_INC(fitness_terminated_traces);
815-
DPRINTF(2, "Fitness terminated: %s(%d) fitness=%d < exit_quality=%d\n",
816-
_PyOpcode_OpName[opcode], oparg, ts->fitness, eq);
817-
goto done;
818-
}
819-
820766
// One for possible _DEOPT, one because _CHECK_VALIDITY itself might _DEOPT
821767
trace->end -= 2;
822768

@@ -870,22 +816,13 @@ _PyJit_translate_single_bytecode_to_trace(
870816
assert(jump_happened ? (next_instr == computed_jump_instr) : (next_instr == computed_next_instr));
871817
uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_happened];
872818
ADD_TO_TRACE(uopcode, 0, 0, INSTR_IP(jump_happened ? computed_next_instr : computed_jump_instr, old_code));
873-
int bp = compute_branch_penalty(target_instr[1].cache, jump_happened);
874-
tracer->translator_state.fitness -= bp;
875-
DPRINTF(3, " branch penalty: -%d (history=0x%04x, taken=%d) -> fitness=%d\n",
876-
bp, target_instr[1].cache, jump_happened,
877-
tracer->translator_state.fitness);
878-
879819
break;
880820
}
881821
case JUMP_BACKWARD_JIT:
882822
// This is possible as the JIT might have re-activated after it was disabled
883823
case JUMP_BACKWARD_NO_JIT:
884824
case JUMP_BACKWARD:
885825
ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target);
886-
tracer->translator_state.fitness -= FITNESS_BACKWARD_EDGE;
887-
DPRINTF(3, " backward edge penalty: -%d -> fitness=%d\n",
888-
FITNESS_BACKWARD_EDGE, tracer->translator_state.fitness);
889826
_Py_FALLTHROUGH;
890827
case JUMP_BACKWARD_NO_INTERRUPT:
891828
{
@@ -1008,44 +945,6 @@ _PyJit_translate_single_bytecode_to_trace(
1008945
assert(next->op.code == STORE_FAST);
1009946
operand = next->op.arg;
1010947
}
1011-
else if (uop == _PUSH_FRAME) {
1012-
_PyJitTracerTranslatorState *ts_depth = &tracer->translator_state;
1013-
ts_depth->frame_depth++;
1014-
if (ts_depth->frame_depth >= MAX_ABSTRACT_FRAME_DEPTH) {
1015-
// The optimizer can't handle frames this deep,
1016-
// so there's no point continuing the trace.
1017-
DPRINTF(2, "Unsupported: frame depth %d >= MAX_ABSTRACT_FRAME_DEPTH\n",
1018-
ts_depth->frame_depth);
1019-
end_trace_opcode = _EXIT_TRACE;
1020-
goto unsupported;
1021-
}
1022-
int32_t frame_penalty = compute_frame_penalty(&tstate->interp->opt_config);
1023-
int32_t cost = frame_penalty * ts_depth->frame_depth;
1024-
ts_depth->fitness -= cost;
1025-
DPRINTF(3, " _PUSH_FRAME: depth=%d, penalty=-%d (per_frame=%d) -> fitness=%d\n",
1026-
ts_depth->frame_depth, cost, frame_penalty,
1027-
ts_depth->fitness);
1028-
}
1029-
else if (uop == _RETURN_VALUE || uop == _RETURN_GENERATOR || uop == _YIELD_VALUE) {
1030-
_PyJitTracerTranslatorState *ts_depth = &tracer->translator_state;
1031-
int32_t frame_penalty = compute_frame_penalty(&tstate->interp->opt_config);
1032-
if (ts_depth->frame_depth <= 0) {
1033-
// Underflow: returning from a frame we didn't enter
1034-
ts_depth->fitness -= frame_penalty * 2;
1035-
DPRINTF(3, " %s: underflow penalty=-%d -> fitness=%d\n",
1036-
_PyOpcode_uop_name[uop], frame_penalty * 2,
1037-
ts_depth->fitness);
1038-
}
1039-
else {
1040-
// Reward returning: small inlined calls should be encouraged
1041-
ts_depth->fitness += frame_penalty;
1042-
DPRINTF(3, " %s: return reward=+%d, depth=%d -> fitness=%d\n",
1043-
_PyOpcode_uop_name[uop], frame_penalty,
1044-
ts_depth->frame_depth - 1,
1045-
ts_depth->fitness);
1046-
}
1047-
ts_depth->frame_depth = ts_depth->frame_depth <= 0 ? 0 : ts_depth->frame_depth - 1;
1048-
}
1049948
else if (_PyUop_Flags[uop] & HAS_RECORDS_VALUE_FLAG) {
1050949
PyObject *recorded_value = tracer->prev_state.recorded_value;
1051950
tracer->prev_state.recorded_value = NULL;
@@ -1087,13 +986,7 @@ _PyJit_translate_single_bytecode_to_trace(
1087986
ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0);
1088987
goto done;
1089988
}
1090-
// Update fitness AFTER translation, BEFORE returning to continue tracing.
1091-
// This ensures the next iteration's fitness check reflects the cost of
1092-
// all instructions translated so far.
1093-
tracer->translator_state.fitness -= FITNESS_PER_INSTRUCTION;
1094-
DPRINTF(3, " per-insn cost: -%d -> fitness=%d\n",
1095-
FITNESS_PER_INSTRUCTION, tracer->translator_state.fitness);
1096-
DPRINTF(2, "Trace continuing (fitness=%d)\n", tracer->translator_state.fitness);
989+
DPRINTF(2, "Trace continuing\n");
1097990
return 1;
1098991
done:
1099992
DPRINTF(2, "Trace done\n");
@@ -1176,17 +1069,6 @@ _PyJit_TryInitializeTracing(
11761069
assert(curr_instr->op.code == JUMP_BACKWARD_JIT || curr_instr->op.code == RESUME_CHECK_JIT || (exit != NULL));
11771070
tracer->initial_state.jump_backward_instr = curr_instr;
11781071

1179-
// Initialize fitness tracking state
1180-
const _PyOptimizationConfig *cfg = &tstate->interp->opt_config;
1181-
_PyJitTracerTranslatorState *ts = &tracer->translator_state;
1182-
bool is_side_trace = (exit != NULL);
1183-
ts->fitness = is_side_trace
1184-
? (int32_t)cfg->fitness_initial_side
1185-
: (int32_t)cfg->fitness_initial;
1186-
ts->frame_depth = 0;
1187-
DPRINTF(3, "Fitness init: %s trace, fitness=%d\n",
1188-
is_side_trace ? "side" : "root", ts->fitness);
1189-
11901072
tracer->is_tracing = true;
11911073
return 1;
11921074
}

Python/pystate.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -635,22 +635,6 @@ init_interpreter(PyInterpreterState *interp,
635635
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
636636
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
637637

638-
// Trace fitness configuration
639-
init_policy(&interp->opt_config.fitness_initial,
640-
"PYTHON_JIT_FITNESS_INITIAL",
641-
FITNESS_INITIAL, 100, 10000);
642-
init_policy(&interp->opt_config.fitness_initial_side,
643-
"PYTHON_JIT_FITNESS_INITIAL_SIDE",
644-
FITNESS_INITIAL_SIDE, 50, 5000);
645-
/* The tracer starts at start_instr, so initial fitness must not be below
646-
* the close-loop exit quality or tracing will terminate immediately. */
647-
if (interp->opt_config.fitness_initial < EXIT_QUALITY_CLOSE_LOOP) {
648-
interp->opt_config.fitness_initial = EXIT_QUALITY_CLOSE_LOOP;
649-
}
650-
if (interp->opt_config.fitness_initial_side < EXIT_QUALITY_CLOSE_LOOP) {
651-
interp->opt_config.fitness_initial_side = EXIT_QUALITY_CLOSE_LOOP;
652-
}
653-
654638
interp->opt_config.specialization_enabled = !is_env_enabled("PYTHON_SPECIALIZATION_OFF");
655639
interp->opt_config.uops_optimize_enabled = !is_env_disabled("PYTHON_UOPS_OPTIMIZE");
656640
if (interp != &runtime->_main_interpreter) {

Python/pystats.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ print_optimization_stats(FILE *out, OptimizationStats *stats)
274274
fprintf(out, "Optimization low confidence: %" PRIu64 "\n", stats->low_confidence);
275275
fprintf(out, "Optimization unknown callee: %" PRIu64 "\n", stats->unknown_callee);
276276
fprintf(out, "Executors invalidated: %" PRIu64 "\n", stats->executors_invalidated);
277-
fprintf(out, "Optimization fitness terminated: %" PRIu64 "\n", stats->fitness_terminated_traces);
278277

279278
print_histogram(out, "Trace length", stats->trace_length_hist);
280279
print_histogram(out, "Trace run length", stats->trace_run_length_hist);

0 commit comments

Comments
 (0)