Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
remove _PyCounterOptimizerObject
  • Loading branch information
xuantengh committed Jan 14, 2025
commit 44aa7abee8211dc77a3e0f6826514c2495fd53af
5 changes: 0 additions & 5 deletions Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,6 @@ struct _PyOptimizerObject {
};

/** Test support **/
typedef struct {
_PyOptimizerObject base;
int64_t count;
} _PyCounterOptimizerObject;

_PyOptimizerObject *_Py_SetOptimizer(PyInterpreterState *interp, _PyOptimizerObject* optimizer);


Expand Down
168 changes: 0 additions & 168 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,91 +35,6 @@ def clear_executors(func):
func.__code__ = func.__code__.replace()


@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer") and
hasattr(_testinternalcapi, "new_counter_optimizer"),
"Requires optimizer infrastructure")
class TestOptimizerAPI(unittest.TestCase):

def test_new_counter_optimizer_dealloc(self):
# See gh-108727
def f():
_testinternalcapi.new_counter_optimizer()

f()

def test_get_set_optimizer(self):
old = _testinternalcapi.get_optimizer()
opt = _testinternalcapi.new_counter_optimizer()
try:
_testinternalcapi.set_optimizer(opt)
self.assertEqual(_testinternalcapi.get_optimizer(), opt)
_testinternalcapi.set_optimizer(None)
self.assertEqual(_testinternalcapi.get_optimizer(), None)
finally:
_testinternalcapi.set_optimizer(old)


def test_counter_optimizer(self):
# Generate a new function at each call
ns = {}
exec(textwrap.dedent(f"""
def loop():
for _ in range({TIER2_THRESHOLD + 1000}):
pass
"""), ns, ns)
loop = ns['loop']

for repeat in range(5):
opt = _testinternalcapi.new_counter_optimizer()
with temporary_optimizer(opt):
self.assertEqual(opt.get_count(), 0)
with clear_executors(loop):
loop()
self.assertEqual(opt.get_count(), 1001)

def test_long_loop(self):
"Check that we aren't confused by EXTENDED_ARG"

# Generate a new function at each call
ns = {}
exec(textwrap.dedent(f"""
def nop():
pass

def long_loop():
for _ in range({TIER2_THRESHOLD + 20}):
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop();
"""), ns, ns)
long_loop = ns['long_loop']

opt = _testinternalcapi.new_counter_optimizer()
with temporary_optimizer(opt):
self.assertEqual(opt.get_count(), 0)
long_loop()
self.assertEqual(opt.get_count(), 21) # Need iterations to warm up

def test_code_restore_for_ENTER_EXECUTOR(self):
def testfunc(x):
i = 0
while i < x:
i += 1

opt = _testinternalcapi.new_counter_optimizer()
with temporary_optimizer(opt):
testfunc(1000)
code, replace_code = testfunc.__code__, testfunc.__code__.replace()
self.assertEqual(code, replace_code)
self.assertEqual(hash(code), hash(replace_code))


def get_first_executor(func):
code = func.__code__
co_code = code.co_code
Expand All @@ -140,89 +55,6 @@ def get_opnames(ex):
return list(iter_opnames(ex))


@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer") and
hasattr(_testinternalcapi, "new_counter_optimizer"),
"Requires optimizer infrastructure")
class TestExecutorInvalidation(unittest.TestCase):

def setUp(self):
self.old = _testinternalcapi.get_optimizer()
self.opt = _testinternalcapi.new_counter_optimizer()
_testinternalcapi.set_optimizer(self.opt)

def tearDown(self):
_testinternalcapi.set_optimizer(self.old)

def test_invalidate_object(self):
# Generate a new set of functions at each call
ns = {}
func_src = "\n".join(
f"""
def f{n}():
for _ in range({TIER2_THRESHOLD}):
pass
""" for n in range(5)
)
exec(textwrap.dedent(func_src), ns, ns)
funcs = [ ns[f'f{n}'] for n in range(5)]
objects = [object() for _ in range(5)]

for f in funcs:
f()
executors = [get_first_executor(f) for f in funcs]
# Set things up so each executor depends on the objects
# with an equal or lower index.
for i, exe in enumerate(executors):
self.assertTrue(exe.is_valid())
for obj in objects[:i+1]:
_testinternalcapi.add_executor_dependency(exe, obj)
self.assertTrue(exe.is_valid())
# Assert that the correct executors are invalidated
# and check that nothing crashes when we invalidate
# an executor multiple times.
for i in (4,3,2,1,0):
_testinternalcapi.invalidate_executors(objects[i])
for exe in executors[i:]:
self.assertFalse(exe.is_valid())
for exe in executors[:i]:
self.assertTrue(exe.is_valid())

def test_uop_optimizer_invalidation(self):
# Generate a new function at each call
ns = {}
exec(textwrap.dedent(f"""
def f():
for i in range({TIER2_THRESHOLD}):
pass
"""), ns, ns)
f = ns['f']
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
f()
exe = get_first_executor(f)
self.assertIsNotNone(exe)
self.assertTrue(exe.is_valid())
_testinternalcapi.invalidate_executors(f.__code__)
self.assertFalse(exe.is_valid())

def test_sys__clear_internal_caches(self):
def f():
for _ in range(TIER2_THRESHOLD):
pass
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
f()
exe = get_first_executor(f)
self.assertIsNotNone(exe)
self.assertTrue(exe.is_valid())
sys._clear_internal_caches()
self.assertFalse(exe.is_valid())
exe = get_first_executor(f)
self.assertIsNone(exe)


@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
Expand Down
12 changes: 0 additions & 12 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1352,18 +1352,6 @@ PyTypeObject _PyCounterExecutor_Type = {
.tp_clear = (inquiry)executor_clear,
};

static PyObject *
counter_get_counter(PyObject *self, PyObject *args)
{
return PyLong_FromLongLong(((_PyCounterOptimizerObject *)self)->count);
}

static PyMethodDef counter_optimizer_methods[] = {
{ "get_count", counter_get_counter, METH_NOARGS, NULL },
{ NULL, NULL },
};


/*****************************************
* Executor management
****************************************/
Expand Down