mirror of
https://github.com/python/cpython.git
synced 2025-08-28 20:56:54 +00:00
GH-126892: Reset warmup counters when JIT compiling code (GH-126893)
This commit is contained in:
parent
addb225f38
commit
48c50ff1a2
6 changed files with 48 additions and 39 deletions
|
@ -91,7 +91,10 @@ def strace_python(code, strace_flags, check=True):
|
||||||
res, cmd_line = run_python_until_end(
|
res, cmd_line = run_python_until_end(
|
||||||
"-c",
|
"-c",
|
||||||
textwrap.dedent(code),
|
textwrap.dedent(code),
|
||||||
__run_using_command=[_strace_binary] + strace_flags)
|
__run_using_command=[_strace_binary] + strace_flags,
|
||||||
|
# Don't want to trace our JIT's own mmap and mprotect calls:
|
||||||
|
PYTHON_JIT="0",
|
||||||
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
return _make_error("Caught OSError", err)
|
return _make_error("Caught OSError", err)
|
||||||
|
|
||||||
|
|
|
@ -1385,20 +1385,17 @@ class TestUopsOptimization(unittest.TestCase):
|
||||||
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
|
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
|
||||||
self.assertEqual(guard_type_version_count, 1)
|
self.assertEqual(guard_type_version_count, 1)
|
||||||
|
|
||||||
def test_guard_type_version_not_removed(self):
|
def test_guard_type_version_removed_invalidation(self):
|
||||||
"""
|
|
||||||
Verify that the guard type version is not removed if we modify the class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def thing(a):
|
def thing(a):
|
||||||
x = 0
|
x = 0
|
||||||
for i in range(TIER2_THRESHOLD + 100):
|
for i in range(TIER2_THRESHOLD * 2 + 1):
|
||||||
x += a.attr
|
x += a.attr
|
||||||
# for the first (TIER2_THRESHOLD + 90) iterations we set the attribute on this dummy function which shouldn't
|
# The first TIER2_THRESHOLD iterations we set the attribute on
|
||||||
# trigger the type watcher
|
# this dummy class, which shouldn't trigger the type watcher.
|
||||||
# then after for the next 10 it should trigger it and stop optimizing
|
# Note that the code needs to be in this weird form so it's
|
||||||
# Note that the code needs to be in this weird form so it's optimized inline without any control flow
|
# optimized inline without any control flow:
|
||||||
setattr((Foo, Bar)[i < TIER2_THRESHOLD + 90], "attr", 2)
|
setattr((Bar, Foo)[i == TIER2_THRESHOLD + 1], "attr", 2)
|
||||||
x += a.attr
|
x += a.attr
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
@ -1410,24 +1407,21 @@ class TestUopsOptimization(unittest.TestCase):
|
||||||
|
|
||||||
res, ex = self._run_with_optimizer(thing, Foo())
|
res, ex = self._run_with_optimizer(thing, Foo())
|
||||||
opnames = list(iter_opnames(ex))
|
opnames = list(iter_opnames(ex))
|
||||||
|
|
||||||
self.assertIsNotNone(ex)
|
self.assertIsNotNone(ex)
|
||||||
self.assertEqual(res, (TIER2_THRESHOLD * 2) + 219)
|
self.assertEqual(res, TIER2_THRESHOLD * 6 + 1)
|
||||||
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
|
call = opnames.index("_CALL_BUILTIN_FAST")
|
||||||
self.assertEqual(guard_type_version_count, 2)
|
load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call)
|
||||||
|
load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call)
|
||||||
|
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
|
||||||
|
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 1)
|
||||||
|
|
||||||
|
def test_guard_type_version_removed_escaping(self):
|
||||||
@unittest.expectedFailure
|
|
||||||
def test_guard_type_version_not_removed_escaping(self):
|
|
||||||
"""
|
|
||||||
Verify that the guard type version is not removed if have an escaping function
|
|
||||||
"""
|
|
||||||
|
|
||||||
def thing(a):
|
def thing(a):
|
||||||
x = 0
|
x = 0
|
||||||
for i in range(100):
|
for i in range(TIER2_THRESHOLD):
|
||||||
x += a.attr
|
x += a.attr
|
||||||
# eval should be escaping and so should cause optimization to stop and preserve both type versions
|
# eval should be escaping
|
||||||
eval("None")
|
eval("None")
|
||||||
x += a.attr
|
x += a.attr
|
||||||
return x
|
return x
|
||||||
|
@ -1437,12 +1431,12 @@ class TestUopsOptimization(unittest.TestCase):
|
||||||
res, ex = self._run_with_optimizer(thing, Foo())
|
res, ex = self._run_with_optimizer(thing, Foo())
|
||||||
opnames = list(iter_opnames(ex))
|
opnames = list(iter_opnames(ex))
|
||||||
self.assertIsNotNone(ex)
|
self.assertIsNotNone(ex)
|
||||||
self.assertEqual(res, 200)
|
self.assertEqual(res, TIER2_THRESHOLD * 2)
|
||||||
guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION")
|
call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS")
|
||||||
# Note: This will actually be 1 for noe
|
load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call)
|
||||||
# https://github.com/python/cpython/pull/119365#discussion_r1626220129
|
load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call)
|
||||||
self.assertEqual(guard_type_version_count, 2)
|
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
|
||||||
|
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 1)
|
||||||
|
|
||||||
def test_guard_type_version_executor_invalidated(self):
|
def test_guard_type_version_executor_invalidated(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Require cold or invalidated code to "warm up" before being JIT compiled
|
||||||
|
again.
|
|
@ -2624,15 +2624,16 @@ dummy_func(
|
||||||
}
|
}
|
||||||
_PyExecutorObject *executor;
|
_PyExecutorObject *executor;
|
||||||
int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0);
|
int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0);
|
||||||
ERROR_IF(optimized < 0, error);
|
if (optimized <= 0) {
|
||||||
if (optimized) {
|
this_instr[1].counter = restart_backoff_counter(counter);
|
||||||
|
ERROR_IF(optimized < 0, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this_instr[1].counter = initial_jump_backoff_counter();
|
||||||
assert(tstate->previous_executor == NULL);
|
assert(tstate->previous_executor == NULL);
|
||||||
tstate->previous_executor = Py_None;
|
tstate->previous_executor = Py_None;
|
||||||
GOTO_TIER_TWO(executor);
|
GOTO_TIER_TWO(executor);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this_instr[1].counter = restart_backoff_counter(counter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
||||||
|
@ -4875,6 +4876,9 @@ dummy_func(
|
||||||
tstate->previous_executor = (PyObject *)current_executor;
|
tstate->previous_executor = (PyObject *)current_executor;
|
||||||
GOTO_TIER_ONE(target);
|
GOTO_TIER_ONE(target);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
exit->temperature = initial_temperature_backoff_counter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exit->executor = executor;
|
exit->executor = executor;
|
||||||
}
|
}
|
||||||
|
|
3
Python/executor_cases.c.h
generated
3
Python/executor_cases.c.h
generated
|
@ -5750,6 +5750,9 @@
|
||||||
tstate->previous_executor = (PyObject *)current_executor;
|
tstate->previous_executor = (PyObject *)current_executor;
|
||||||
GOTO_TIER_ONE(target);
|
GOTO_TIER_ONE(target);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
exit->temperature = initial_temperature_backoff_counter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exit->executor = executor;
|
exit->executor = executor;
|
||||||
}
|
}
|
||||||
|
|
13
Python/generated_cases.c.h
generated
13
Python/generated_cases.c.h
generated
|
@ -5092,15 +5092,18 @@
|
||||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||||
int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0);
|
int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0);
|
||||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||||
if (optimized < 0) goto error;
|
if (optimized <= 0) {
|
||||||
if (optimized) {
|
this_instr[1].counter = restart_backoff_counter(counter);
|
||||||
|
if (optimized < 0) goto error;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||||
|
this_instr[1].counter = initial_jump_backoff_counter();
|
||||||
|
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||||
assert(tstate->previous_executor == NULL);
|
assert(tstate->previous_executor == NULL);
|
||||||
tstate->previous_executor = Py_None;
|
tstate->previous_executor = Py_None;
|
||||||
GOTO_TIER_TWO(executor);
|
GOTO_TIER_TWO(executor);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this_instr[1].counter = restart_backoff_counter(counter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue