Skip to content

gh-146031: Allow keeping specialization enabled when specifying eval frame function#146032

Open
DinoV wants to merge 2 commits intopython:mainfrom
DinoV:allow_specialization
Open

gh-146031: Allow keeping specialization enabled when specifying eval frame function#146032
DinoV wants to merge 2 commits intopython:mainfrom
DinoV:allow_specialization

Conversation

@DinoV
Copy link
Copy Markdown
Contributor

@DinoV DinoV commented Mar 16, 2026

An eval frame evaluator doesn't necessarily want to disable specialization of Python -> Python calls... The custom eval frame can either hook the specializing functions its self or they can specify vectorcall on a function object which prevents the nested dispatch.

This adds an argument to _PyInterpreterState_SetEvalFrameFunc which allows the specification of whether or not specialization should be allowed. Setting it to 1 will allow the specializer to optimize Python -> Python calls. It's up to the eval frame replacement to make sure that this will work for them.


📚 Documentation preview 📚: https://cpython-previews--146032.org.readthedocs.build/

@DinoV DinoV marked this pull request as draft March 16, 2026 17:25
@DinoV DinoV force-pushed the allow_specialization branch 3 times, most recently from 3adb88b to 3c76262 Compare March 16, 2026 18:23
@DinoV DinoV marked this pull request as ready for review March 16, 2026 19:18
@DinoV DinoV force-pushed the allow_specialization branch from 3c76262 to f3b7b90 Compare March 20, 2026 18:02
@markshannon
Copy link
Copy Markdown
Member

With the flag set, some calls to any given Python function will go through the custom function and some won't.
There will be no obvious pattern, as it depends whether the caller happens to be written in Python or in C.
Is this what you want?

As soon as any function is run in the interpreter, every function called by it (transitively) could be invisible to the custom frame evaluator.

@DinoV
Copy link
Copy Markdown
Contributor Author

DinoV commented Mar 26, 2026

With the flag set, some calls to any given Python function will go through the custom function and some won't. There will be no obvious pattern, as it depends whether the caller happens to be written in Python or in C. Is this what you want?

As soon as any function is run in the interpreter, every function called by it (transitively) could be invisible to the custom frame evaluator.

Yep, it's then up to the replacement interpreter to decide what it wants to do here. If they're just a wrapper than they probably don't want to set the flag. If they're actually interpreting the byte code than they are responsible for the implementation of the inlined calls and can dispatch to them in their own inlined interpreter.

@markshannon
Copy link
Copy Markdown
Member

This seems reasonable.

The performance impact should be zero for CPython (as the new flag is only checked if PEP 523 is active).
If you add a new function to set the flag, the PEP 523 interface will be unchanged.

@@ -318,4 +318,7 @@ PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
PyInterpreterState *interp);
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to change this API again. It break PyTorch every time we do so.

Maybe add another function to set the flag.

JUMP_TO_LABEL(start_frame); \
#define DISPATCH_INLINED(NEW_FRAME) \
do { \
assert(tstate->interp->eval_frame == NULL || \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use your IS_PEP523_HOOKED macro here?

Comment on lines +2924 to +2929
func_calls = [c for c in actual_calls if c == "func"]
self.assertEqual(len(func_calls), 0)

# But the normal interpreter loop still shouldn't be inlining things
func_outer_calls = [c for c in actual_calls if c == "func_outer"]
self.assertNotEqual(len(func_outer_calls), 0)
Copy link
Copy Markdown
Contributor

@ashm-dev ashm-dev Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to use the .count method of arrays here. I.e. self.assertEqual(actual_calls.count('func'), 0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants