gh-91053: Add an optional callback that is invoked whenever a function is modified#98175
gh-91053: Add an optional callback that is invoked whenever a function is modified#98175erlend-aasland merged 16 commits intopython:mainfrom
Conversation
JIT compilers may need to invalidate compiled code when a function is modified (e.g. if its code object is modified). This adds the ability to set a callback that, when set, is called each time a function is modified.
|
pyperformance results are perf-neutral. I also wrote a microbenchmark that attempts to measure the overhead on function creation when no callbacks are present. Happy to use a different benchmark and/or do this a bit more rigorously if there is concern. Results on the microbenchmark are perf-neutral as well: This PR with no function watchers set: Upstream: All benchmarks were run on a bare metal machine in AWS that was configured using |
|
The failing test appears unrelated to this diff. Is there a way that I can re-run the test without adding additional commits? |
You can merge in |
Or any core dev (and probably triager) can trigger a rerun, which I did already and it's now passed. |
erlend-aasland
left a comment
There was a problem hiding this comment.
Looks good. I added a bunch of PEP 7 comments because we should (almost) always strive to follow the style guide, both for new code and (in most cases) when modifying existing code. I also added some comments regarding docs, inconsistent naming of the constants, and some C API suggestions.
Questions:
PyFunction_SetDefaultsshould emit aPyFunction_EVENT_MODIFY_DEFAULTSevent, no?PyFunction_SetKwDefaultsshould emit aPyFunction_EVENT_MODIFY_KWDEFAULTSevent, no?- Why no events for
func_closureandfunc_annotationsmodifications?
Also, please add the Python test code to Lib/test/test_capi.py instead of adding a new Lib/test/test_func_events.py, like the dict watchers did.
Yep, good catch!
Sure thing, will do. |
- Imperative mood - Semantic line breaks
erlend-aasland
left a comment
There was a problem hiding this comment.
Thanks for making the changes! I've got one last minor style nit. Also please merge in main and resolve the conflicts.
|
I don't like having watchers for function creation and destruction. Having those watchers adds overhead for every creation of a comprehension or nested function, which could be an issue for performance for two reasons:
|
|
Discussed offline with @markshannon and reached these conclusions (Mark please let me know if any of this doesn't match your understanding):
We also discussed a possible alternative design where code objects have a vectorcall pointer and function objects just copy it from the code object; then we wouldn't need func-created hook. But this also adds an extra read to function creation, as well as making code objects bigger, so it's not clear this is really a better approach. |
A bit is set in the bit vector iff there is a watcher set at the corresponding offset in the watcher array. Only notify watchers if at least one bit is set.
|
LGTM, thanks! I'll wait for @markshannon's thumbs up before merging. |
Summary: Backport function watchers from upstream 3.12 (cf python/cpython#98175, although I backported from latest 3.12 branch, which has had a few modifications.) This diff doesn't use the new API yet; I'll do that in a separate stacked diff. Reviewed By: DinoV Differential Revision: D46993187 fbshipit-source-id: 35f3a0f86f6f0e2764205d4c865e1fb5efb69314
JIT compilers may need to invalidate compiled code when a function is modified (e.g. if its code object is modified). This adds the ability to set a callback that, when set, is called each time a function is modified.