Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5816199

Browse files
authored
GH-104584: Assorted fixes for the optimizer API. (GH-105683)
* Add test for long loops * Clear ENTER_EXECUTOR when deopting code objects.
1 parent 4426279 commit 5816199

File tree

6 files changed

+297
-203
lines changed

6 files changed

+297
-203
lines changed

Lib/test/test_capi/test_misc.py

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,17 +2347,66 @@ def func():
23472347

23482348
class TestOptimizerAPI(unittest.TestCase):
23492349

2350-
def test_counter_optimizer(self):
2351-
opt = _testinternalcapi.get_counter_optimizer()
2352-
self.assertEqual(opt.get_count(), 0)
2350+
@contextlib.contextmanager
2351+
def temporary_optimizer(self, opt):
2352+
_testinternalcapi.set_optimizer(opt)
23532353
try:
2354-
_testinternalcapi.set_optimizer(opt)
2355-
self.assertEqual(opt.get_count(), 0)
2356-
for _ in range(1000):
2357-
pass
2358-
self.assertEqual(opt.get_count(), 1000)
2354+
yield
23592355
finally:
23602356
_testinternalcapi.set_optimizer(None)
23612357

2358+
@contextlib.contextmanager
2359+
def clear_executors(self, func):
2360+
try:
2361+
yield
2362+
finally:
2363+
#Clear executors
2364+
func.__code__ = func.__code__.replace()
2365+
2366+
def test_get_set_optimizer(self):
2367+
self.assertEqual(_testinternalcapi.get_optimizer(), None)
2368+
opt = _testinternalcapi.get_counter_optimizer()
2369+
_testinternalcapi.set_optimizer(opt)
2370+
self.assertEqual(_testinternalcapi.get_optimizer(), opt)
2371+
_testinternalcapi.set_optimizer(None)
2372+
self.assertEqual(_testinternalcapi.get_optimizer(), None)
2373+
2374+
def test_counter_optimizer(self):
2375+
2376+
def loop():
2377+
for _ in range(1000):
2378+
pass
2379+
2380+
for repeat in range(5):
2381+
opt = _testinternalcapi.get_counter_optimizer()
2382+
with self.temporary_optimizer(opt):
2383+
self.assertEqual(opt.get_count(), 0)
2384+
with self.clear_executors(loop):
2385+
loop()
2386+
self.assertEqual(opt.get_count(), 1000)
2387+
2388+
def test_long_loop(self):
2389+
"Check that we aren't confused by EXTENDED_ARG"
2390+
2391+
def nop():
2392+
pass
2393+
2394+
def long_loop():
2395+
for _ in range(10):
2396+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2397+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2398+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2399+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2400+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2401+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2402+
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
2403+
2404+
opt = _testinternalcapi.get_counter_optimizer()
2405+
with self.temporary_optimizer(opt):
2406+
self.assertEqual(opt.get_count(), 0)
2407+
long_loop()
2408+
self.assertEqual(opt.get_count(), 10)
2409+
2410+
23622411
if __name__ == "__main__":
23632412
unittest.main()

Modules/_testinternalcapi.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,15 @@ set_optimizer(PyObject *self, PyObject *opt)
840840
Py_RETURN_NONE;
841841
}
842842

843+
static PyObject *
844+
get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored))
845+
{
846+
PyObject *opt = (PyObject *)PyUnstable_GetOptimizer();
847+
if (opt == NULL) {
848+
Py_RETURN_NONE;
849+
}
850+
return opt;
851+
}
843852

844853
static int _pending_callback(void *arg)
845854
{
@@ -982,6 +991,7 @@ static PyMethodDef module_functions[] = {
982991
{"iframe_getcode", iframe_getcode, METH_O, NULL},
983992
{"iframe_getline", iframe_getline, METH_O, NULL},
984993
{"iframe_getlasti", iframe_getlasti, METH_O, NULL},
994+
{"get_optimizer", get_optimizer, METH_NOARGS, NULL},
985995
{"set_optimizer", set_optimizer, METH_O, NULL},
986996
{"get_counter_optimizer", get_counter_optimizer, METH_NOARGS, NULL},
987997
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),

Objects/codeobject.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,12 +1465,28 @@ PyCode_GetFreevars(PyCodeObject *code)
14651465
return _PyCode_GetFreevars(code);
14661466
}
14671467

1468+
static void
1469+
clear_executors(PyCodeObject *co)
1470+
{
1471+
for (int i = 0; i < co->co_executors->size; i++) {
1472+
Py_CLEAR(co->co_executors->executors[i]);
1473+
}
1474+
PyMem_Free(co->co_executors);
1475+
co->co_executors = NULL;
1476+
}
1477+
14681478
static void
14691479
deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions)
14701480
{
14711481
Py_ssize_t len = Py_SIZE(code);
14721482
for (int i = 0; i < len; i++) {
14731483
int opcode = _Py_GetBaseOpcode(code, i);
1484+
if (opcode == ENTER_EXECUTOR) {
1485+
_PyExecutorObject *exec = code->co_executors->executors[instructions[i].op.arg];
1486+
opcode = exec->vm_data.opcode;
1487+
instructions[i].op.arg = exec->vm_data.oparg;
1488+
}
1489+
assert(opcode != ENTER_EXECUTOR);
14741490
int caches = _PyOpcode_Caches[opcode];
14751491
instructions[i].op.code = opcode;
14761492
for (int j = 1; j <= caches; j++) {
@@ -1679,10 +1695,7 @@ code_dealloc(PyCodeObject *co)
16791695
PyMem_Free(co_extra);
16801696
}
16811697
if (co->co_executors != NULL) {
1682-
for (int i = 0; i < co->co_executors->size; i++) {
1683-
Py_CLEAR(co->co_executors->executors[i]);
1684-
}
1685-
PyMem_Free(co->co_executors);
1698+
clear_executors(co);
16861699
}
16871700

16881701
Py_XDECREF(co->co_consts);
@@ -2278,6 +2291,9 @@ void
22782291
_PyStaticCode_Fini(PyCodeObject *co)
22792292
{
22802293
deopt_code(co, _PyCode_CODE(co));
2294+
if (co->co_executors != NULL) {
2295+
clear_executors(co);
2296+
}
22812297
PyMem_Free(co->co_extra);
22822298
if (co->_co_cached != NULL) {
22832299
Py_CLEAR(co->_co_cached->_co_code);

Python/bytecodes.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,7 @@ dummy_func(
21572157
frame = cframe.current_frame;
21582158
goto error;
21592159
}
2160+
assert(frame == cframe.current_frame);
21602161
here[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) -1);
21612162
goto resume_frame;
21622163
}
@@ -2176,7 +2177,7 @@ dummy_func(
21762177

21772178
inst(ENTER_EXECUTOR, (--)) {
21782179
PyCodeObject *code = _PyFrame_GetCode(frame);
2179-
_PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg];
2180+
_PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255];
21802181
Py_INCREF(executor);
21812182
frame = executor->execute(executor, frame, stack_pointer);
21822183
if (frame == NULL) {

0 commit comments

Comments
 (0)