-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
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
bpo-46564: Optimize super().meth()
calls via adaptive superinstructions
#30992
Conversation
super().meth()
callssuper().meth()
calls via adaptive superinstructions
Marking as draft as I need make this work with the new |
|
||
DEOPT_IF(_PyType_CAST(super_callable) != &PySuper_Type, CALL); | ||
/* super() - zero argument form */ | ||
if (_PySuper_GetTypeArgs(frame, frame->f_code, &su_type, &su_obj) < 0) { |
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.
Can't we do this at specialization time? The number of locals, the index of "self", and whether it is a cell are all known then. Likewise the nature of __class__
is also known.
} | ||
assert(su_obj != NULL); | ||
DEOPT_IF(lm_adaptive->version != Py_TYPE(su_obj)->tp_version_tag, CALL); | ||
DEOPT_IF(cache0->version != su_type->tp_version_tag, CALL); |
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.
When can this fail?
Isn't the next item in the MRO determined solely by type(self)
and __class__
, both of which are known at this point?
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 wanted assurance that __class__
didn't change.. Then again, I'm not sure if it can?
Maybe we should merge #31002 first, as that PR is simpler. |
👍 |
Mark, I'm going to run benchmarks on |
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 couple of indentation-related inconsistencies:
static int | ||
super_init_without_args(InterpreterFrame *cframe, PyCodeObject *co, | ||
int | ||
_PySuper_GetTypeArgs(InterpreterFrame *cframe, PyCodeObject *co, | ||
PyTypeObject **type_p, PyObject **obj_p) |
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.
PyTypeObject **type_p, PyObject **obj_p) | |
PyTypeObject **type_p, PyObject **obj_p) |
The line was aligned with an opening parenthesis of a parameter list.
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, | ||
PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); |
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.
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, | |
PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); | |
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, | |
PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); |
as in a removed line, or even:
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, | |
PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); | |
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins, | |
PyObject **stack_pointer, InterpreterFrame *frame, PyObject *names); |
as in _Py_Specialize_BinaryOp
right below.
Well that was depressing. |
Microbenchmarks show that import timeit
setup = """
class A:
def f(self): pass
class B(A):
def g(self): super().f()
def h(self): self.f()
b = B()
"""
# super() call
print(timeit.timeit("b.g()", setup=setup, number=20_000_000))
# reference
print(timeit.timeit("b.h()", setup=setup, number=20_000_000)) Results:
So |
They should now have almost no overhead over a corresponding
self.meth()
call.Summary of changes:
typeobject.c
-- refactoring to reuse code during specialization, also useInterpreterFrame
overPyFrameObject
for lazy frame benefits. Some changes here are partially taken from bpo-43563 : Introduce dedicated opcodes for super calls #24936. All credits to @vladima (I've tried to properly include them in the news item too.)specialize.c
-- specialize for the 0-argument and 2-argument form ofsuper()
.ceval.c
-- does both aCALL
andLOAD_METHOD
without intermediates (and both are specialized forms too).TODO:
benchmarks!
https://bugs.python.org/issue46564