Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit52e902c

Browse files
authored
GH-109369: Add machinery for deoptimizing tier2 executors, both individually and globally. (GH-110384)
1 parent32c37fe commit52e902c

File tree

7 files changed

+353
-2
lines changed

7 files changed

+353
-2
lines changed

‎Include/cpython/optimizer.h‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,27 @@
66
extern"C" {
77
#endif
88

9+
typedefstruct_PyExecutorLinkListNode {
10+
struct_PyExecutorObject*next;
11+
struct_PyExecutorObject*previous;
12+
}_PyExecutorLinkListNode;
13+
14+
15+
/* Bloom filter with m = 256
16+
* https://en.wikipedia.org/wiki/Bloom_filter */
17+
#defineBLOOM_FILTER_WORDS 8
18+
19+
typedefstruct_bloom_filter {
20+
uint32_tbits[BLOOM_FILTER_WORDS];
21+
}_PyBloomFilter;
22+
923
typedefstruct {
1024
uint8_topcode;
1125
uint8_toparg;
26+
uint8_tvalid;
27+
uint8_tlinked;
28+
_PyBloomFilterbloom;
29+
_PyExecutorLinkListNodelinks;
1230
}_PyVMData;
1331

1432
typedefstruct_PyExecutorObject {
@@ -45,6 +63,14 @@ _PyOptimizer_BackEdge(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_
4563

4664
extern_PyOptimizerObject_PyOptimizer_Default;
4765

66+
void_Py_ExecutorInit(_PyExecutorObject*,_PyBloomFilter*);
67+
void_Py_ExecutorClear(_PyExecutorObject*);
68+
void_Py_BloomFilter_Init(_PyBloomFilter*);
69+
void_Py_BloomFilter_Add(_PyBloomFilter*bloom,void*obj);
70+
PyAPI_FUNC(void)_Py_Executor_DependsOn(_PyExecutorObject*executor,void*obj);
71+
PyAPI_FUNC(void)_Py_Executors_InvalidateDependency(PyInterpreterState*interp,void*obj);
72+
externvoid_Py_Executors_InvalidateAll(PyInterpreterState*interp);
73+
4874
/* For testing */
4975
PyAPI_FUNC(PyObject*)PyUnstable_Optimizer_NewCounter(void);
5076
PyAPI_FUNC(PyObject*)PyUnstable_Optimizer_NewUOpOptimizer(void);

‎Include/internal/pycore_interp.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ struct _is {
215215
structtypes_statetypes;
216216
structcallable_cachecallable_cache;
217217
_PyOptimizerObject*optimizer;
218+
_PyExecutorObject*executor_list_head;
218219
uint16_toptimizer_resume_threshold;
219220
uint16_toptimizer_backedge_threshold;
220221
uint32_tnext_func_version;

‎Lib/test/test_capi/test_misc.py‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,67 @@ def get_first_executor(func):
24892489
returnNone
24902490

24912491

2492+
classTestExecutorInvalidation(unittest.TestCase):
2493+
2494+
defsetUp(self):
2495+
self.old=_testinternalcapi.get_optimizer()
2496+
self.opt=_testinternalcapi.get_counter_optimizer()
2497+
_testinternalcapi.set_optimizer(self.opt)
2498+
2499+
deftearDown(self):
2500+
_testinternalcapi.set_optimizer(self.old)
2501+
2502+
deftest_invalidate_object(self):
2503+
# Generate a new set of functions at each call
2504+
ns= {}
2505+
func_src="\n".join(
2506+
f"""
2507+
def f{n}():
2508+
for _ in range(1000):
2509+
pass
2510+
"""forninrange(5)
2511+
)
2512+
exec(textwrap.dedent(func_src),ns,ns)
2513+
funcs= [ns[f'f{n}']forninrange(5)]
2514+
objects= [object()for_inrange(5)]
2515+
2516+
forfinfuncs:
2517+
f()
2518+
executors= [get_first_executor(f)forfinfuncs]
2519+
# Set things up so each executor depends on the objects
2520+
# with an equal or lower index.
2521+
fori,exeinenumerate(executors):
2522+
self.assertTrue(exe.valid)
2523+
forobjinobjects[:i+1]:
2524+
_testinternalcapi.add_executor_dependency(exe,obj)
2525+
self.assertTrue(exe.valid)
2526+
# Assert that the correct executors are invalidated
2527+
# and check that nothing crashes when we invalidate
2528+
# an executor mutliple times.
2529+
foriin (4,3,2,1,0):
2530+
_testinternalcapi.invalidate_executors(objects[i])
2531+
forexeinexecutors[i:]:
2532+
self.assertFalse(exe.valid)
2533+
forexeinexecutors[:i]:
2534+
self.assertTrue(exe.valid)
2535+
2536+
deftest_uop_optimizer_invalidation(self):
2537+
# Generate a new function at each call
2538+
ns= {}
2539+
exec(textwrap.dedent("""
2540+
def f():
2541+
for i in range(1000):
2542+
pass
2543+
"""),ns,ns)
2544+
f=ns['f']
2545+
opt=_testinternalcapi.get_uop_optimizer()
2546+
withtemporary_optimizer(opt):
2547+
f()
2548+
exe=get_first_executor(f)
2549+
self.assertTrue(exe.valid)
2550+
_testinternalcapi.invalidate_executors(f.__code__)
2551+
self.assertFalse(exe.valid)
2552+
24922553
classTestUops(unittest.TestCase):
24932554

24942555
deftest_basic_loop(self):

‎Modules/_testinternalcapi.c‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,32 @@ get_executor(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
10021002
return (PyObject*)PyUnstable_GetExecutor((PyCodeObject*)code,ioffset);
10031003
}
10041004

1005+
staticPyObject*
1006+
add_executor_dependency(PyObject*self,PyObject*args)
1007+
{
1008+
PyObject*exec;
1009+
PyObject*obj;
1010+
if (!PyArg_ParseTuple(args,"OO",&exec,&obj)) {
1011+
returnNULL;
1012+
}
1013+
/* No way to tell in general if exec is an executor, so we only accept
1014+
* counting_executor */
1015+
if (strcmp(Py_TYPE(exec)->tp_name,"counting_executor")) {
1016+
PyErr_SetString(PyExc_TypeError,"argument must be a counting_executor");
1017+
returnNULL;
1018+
}
1019+
_Py_Executor_DependsOn((_PyExecutorObject*)exec,obj);
1020+
Py_RETURN_NONE;
1021+
}
1022+
1023+
staticPyObject*
1024+
invalidate_executors(PyObject*self,PyObject*obj)
1025+
{
1026+
PyInterpreterState*interp=PyInterpreterState_Get();
1027+
_Py_Executors_InvalidateDependency(interp,obj);
1028+
Py_RETURN_NONE;
1029+
}
1030+
10051031
staticint_pending_callback(void*arg)
10061032
{
10071033
/* we assume the argument is callable object to which we own a reference */
@@ -1565,6 +1591,8 @@ static PyMethodDef module_functions[] = {
15651591
{"get_executor",_PyCFunction_CAST(get_executor),METH_FASTCALL,NULL},
15661592
{"get_counter_optimizer",get_counter_optimizer,METH_NOARGS,NULL},
15671593
{"get_uop_optimizer",get_uop_optimizer,METH_NOARGS,NULL},
1594+
{"add_executor_dependency",add_executor_dependency,METH_VARARGS,NULL},
1595+
{"invalidate_executors",invalidate_executors,METH_O,NULL},
15681596
{"pending_threadfunc",_PyCFunction_CAST(pending_threadfunc),
15691597
METH_VARARGS |METH_KEYWORDS},
15701598
{"pending_identify",pending_identify,METH_VARARGS,NULL},

‎Python/instrumentation.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,7 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp)
15821582
if (code->co_executors!=NULL) {
15831583
_PyCode_Clear_Executors(code);
15841584
}
1585+
_Py_Executors_InvalidateDependency(interp,code);
15851586
intcode_len= (int)Py_SIZE(code);
15861587
/* code->_co_firsttraceable >= code_len indicates
15871588
* that no instrumentation can be inserted.
@@ -1803,6 +1804,7 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events)
18031804
return-1;
18041805
}
18051806
set_global_version(interp,new_version);
1807+
_Py_Executors_InvalidateAll(interp);
18061808
returninstrument_all_executing_code_objects(interp);
18071809
}
18081810

@@ -1832,6 +1834,7 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
18321834
/* Force instrumentation update */
18331835
code->_co_instrumentation_version-=MONITORING_VERSION_INCREMENT;
18341836
}
1837+
_Py_Executors_InvalidateDependency(interp,code);
18351838
if (_Py_Instrument(code,interp)) {
18361839
return-1;
18371840
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp