-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-91049: Introduce set vectorcall field API for PyFunctionObject #92257
gh-91049: Introduce set vectorcall field API for PyFunctionObject #92257
Conversation
Every change to Python requires a NEWS entry. Please, add it using the blurb_it Web app or the blurb command-line tool. |
@markshannon Can you let me know if there should be any other checks in For context, this check fixes an edge case where calling function with a set vectorfield only worked with a unpacked tuple, e.g. f |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for working on this @adphrost !
Python/ceval.c
Outdated
@@ -4792,7 +4792,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
PyObject *function = PEEK(total_args + 1); | |||
int positional_args = total_args - KWNAMES_LEN(); | |||
// Check if the call can be inlined or not | |||
if (Py_TYPE(function) == &PyFunction_Type && tstate->interp->eval_frame == NULL) { | |||
if (Py_TYPE(function) == &PyFunction_Type && tstate->interp->eval_frame == NULL && ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this probably exceeds line length
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the max line length allowed? And is there a linter I can run to handle this for me?
|
||
This PR introduces an API call where vectorcall field can be modified like so: | ||
|
||
`void PyFunction_SetVectorcall(PyFunctionObject *func, vectorcallfunc vectorcall);` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document the function in Doc/c-api/function.rst and in the NEWS entry, refer to use with:
:c:func:`PyFunction_SetVectorcall`
New public functions should also be documented in What's New in Python 3.11 > C API > New features: Doc/whatsnew/3.11.rst.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments.
Have you attempted to measure the performance impact of this? (I'd expect it to be small or negligible).
Change how assertion is done Co-authored-by: Itamar Ostricher <[email protected]>
@markshannon is there a simple way to ask a buildbot to run pyperformance comparing this against the base revision? we can do that manually, but a buildbot doing it for us would be nice :) |
circling back to @markshannon 's performance impact question. -- tldr some more details: rebased this PR onto a4460f2 and ran pyperformance on a4460f2 and on the rebased branch both baseline and branch were built using used pyperformance 1.0.5 (installed directly from GitHub) on bare metal AWS machine (Linux-5.13.0-1023-aws-x86_64-with-glibc2.31 with 72 logical CPUs), and excluded the sqlalchemy benchmarks because they tried building greenlet and failed
compare results:
thanks @ansley for doing the perf analysis on this :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. This change reminds me my old https://peps.python.org/pep-0510/ nice :-)
Modules/_testcapimodule.c
Outdated
static PyObject * | ||
override_vectorcall( | ||
PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) { | ||
Py_RETURN_NONE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you modify the test to return the string "overridden" instead of just returning None?
Doc/whatsnew/3.12.rst
Outdated
@@ -236,6 +236,10 @@ New Features | |||
an additional metaclass argument. | |||
(Contributed by Wenzel Jakob in :gh:`93012`.) | |||
|
|||
* Add new function :c:func:`PyFunction_SetVectorcall` to the C API | |||
which sets the vectorcall field of a given PyFunctionObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may explain the purpose of the function here, like mention Cinder JIT and Pyjion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not have a "purpose". We should state clearly what the function does, not bless some particular use case.
@adphrost
Could you add a warning that the new function pointer must have exactly the same behavior as the unaltered function.
I am a little concerned that C extensions will abuse this, and we will have deal with the bug reports.
CPython extensions providing optimized execution of Python bytecode (like Cinder JIT and Pyjion) | ||
can benefit from being able to modify the vectorcall field on instances of PyFunctionObject to allow calling the optimized path (e.g. JIT-compiled) directly. | ||
|
||
This PR introduces an API call where vectorcall field can be modified like so: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This text lands into https://docs.python.org/dev/whatsnew/changelog.html#changelog which is a changlog, you should not mention "a PR" there.
I suggest to just copy/paste what you wrote in whatsnew.
Doc/whatsnew/3.12.rst
Outdated
@@ -236,6 +236,10 @@ New Features | |||
an additional metaclass argument. | |||
(Contributed by Wenzel Jakob in :gh:`93012`.) | |||
|
|||
* Add new function :c:func:`PyFunction_SetVectorcall` to the C API | |||
which sets the vectorcall field of a given PyFunctionObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which sets the vectorcall field of a given PyFunctionObject | |
which sets the vectorcall field of a given :c:type:`PyFunctionObject`. |
@vstinner @markshannon I think all comments were addressed - any further feedback? |
Doc/c-api/function.rst
Outdated
@@ -83,6 +83,12 @@ There are a few functions specific to Python functions. | |||
Raises :exc:`SystemError` and returns ``-1`` on failure. | |||
|
|||
|
|||
.. c:function:: void PyFunction_SetVectorcall(PyFunctionObject *func, vectorcallfunc vectorcall) | |||
|
|||
Set the vectorcall field of a given function object *func* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set the vectorcall field of a given function object *func* | |
Set the vectorcall field of a given function object *func*. |
Doc/whatsnew/3.12.rst
Outdated
@@ -236,6 +236,12 @@ New Features | |||
an additional metaclass argument. | |||
(Contributed by Wenzel Jakob in :gh:`93012`.) | |||
|
|||
* Add new function :c:func:`PyFunction_SetVectorcall` to the C API | |||
which sets the vectorcall field of a given :c:type:`PyFunctionObject` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which sets the vectorcall field of a given :c:type:`PyFunctionObject` | |
which sets the vectorcall field of a given :c:type:`PyFunctionObject`. |
Doc/whatsnew/3.12.rst
Outdated
* Add new function :c:func:`PyFunction_SetVectorcall` to the C API | ||
which sets the vectorcall field of a given :c:type:`PyFunctionObject` | ||
Warning: extensions using this API must preserve the behavior | ||
of the unaltered function! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this document is the right place to put a warning. Why this warning is not in the documentation of the function?
which sets the vectorcall field of a given :c:type:`PyFunctionObject` | ||
|
||
Warning: extensions using this API must preserve the behavior | ||
of the unaltered function! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this warning.
@@ -0,0 +1,5 @@ | |||
Add new function :c:func:`PyFunction_SetVectorcall` to the C API | |||
which sets the vectorcall field of a given :c:type:`PyFunctionObject` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which sets the vectorcall field of a given :c:type:`PyFunctionObject` | |
which sets the vectorcall field of a given :c:type:`PyFunctionObject`. |
@vstinner @markshannon thanks for the feedback. with @markshannon 's last response I think the remaining feedback is minor styling and phrasing. if that's all that left for this to be merged, I'm happy to perform these changes, but also totally ok with a core dev making these last changes when merging 😄 |
…(replace with assert) - func version check is sufficient
…TRIBUTE_OVERRIDDEN specialization)
pushed a fix to @markshannon I also reviewed the interpreter loop and didn't found any other cases that inline calls without deopting on function version. the methodology I used is searching for occurrences of (btw, I noticed that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parts touching the specializer and specialized opcodes LGTM.
No that's wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One minor formatting nit. Otherwise looks good.
I'll file a separate issue for that |
All looks good to me. |
- PEP-7 braces - add assert - raise TypeError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test LGTM in general but I have a few comments. Thanks!
1. test specialization doesn't trigger when vectorcall is already overridden 2. test specialization gets deopted when vectorcall is overridden after specialization triggered
Almost ready. Who's on the hook to get this merged? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now.
I'll merge once the conflict has been resolved. |
resolved! |
PyFunction_SetVectorcall() is really a cool feature, I love it! I'm not sure yet how people will manage to abuse it, but I love it :-D |
Avoid specializing functions with overridden vectorcall field