-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-44889: Specialize LOAD_METHOD with PEP 659 adaptive interpreter #27722
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
Changes from 1 commit
60ab351
e0f7ebe
ffd7ff3
2b014a2
b200887
d977986
187fddb
c71f142
f3082f4
0f32d60
8a31db3
06d606e
608f6d0
1f5e64a
29daf44
a3d6dd3
d7ee4ac
c944af0
2f5cc63
70ad1ea
f4487fc
afcf51e
e551d7a
6f1f1f0
dc152ed
dd77f75
601e9b8
94b6106
1d87e53
3697d4f
1d83667
5db1d3b
d12205b
8af701d
ad4ff6e
a3d1b50
d8384bc
ae2a520
a12406b
33445d5
6d806a2
020a326
d27e388
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -232,7 +232,7 @@ static uint8_t adaptive_opcodes[256] = { | |
static uint8_t cache_requirements[256] = { | ||
[LOAD_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */ | ||
[LOAD_GLOBAL] = 2, /* _PyAdaptiveEntry and _PyLoadGlobalCache */ | ||
[LOAD_METHOD] = 3, /* _PyAdaptiveEntry and 2 _PyLoadMethodCache */ | ||
[LOAD_METHOD] = 3, /* _PyAdaptiveEntry, _PyAttrCache and _PyObjectCache */ | ||
[BINARY_SUBSCR] = 0, | ||
[STORE_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */ | ||
}; | ||
|
@@ -793,8 +793,8 @@ int | |
_Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache) | ||
{ | ||
_PyAdaptiveEntry *cache0 = &cache->adaptive; | ||
_PyLoadMethodCache *cache1 = &cache[-1].load_method; | ||
_PyLoadMethodCache *cache2 = &cache[-2].load_method; | ||
_PyAttrCache *cache1 = &cache[-1].attr; | ||
_PyObjectCache *cache2 = &cache[-2].obj; | ||
|
||
PyTypeObject *owner_cls = Py_TYPE(owner); | ||
if (PyModule_CheckExact(owner)) { | ||
|
@@ -837,13 +837,13 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, | |
cls_has_dict && | ||
owner_cls->tp_dictoffset >= 0) { | ||
|
||
// if o.__dict__ changes, the method might be found in o.__dict__ | ||
// instead of old type lookup. So record o.__dict__. | ||
// If o.__dict__ changes, the method might be found in o.__dict__ | ||
// instead of old type lookup. So record o.__dict__'s keys. | ||
uint32_t keys_version = UINT32_MAX; | ||
if (owner_has_dict) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code looks similar to the code for checking indexes for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought so too, but upon inspection, the similarity is skin deep. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The specializer shouldn't care whether the If we factor out the code that determines if the class has cached keys and the index of the name: The problem is that there is no API for determining the index of a key in a dictionary-keys, only in a dictionary. |
||
// _PyDictKeys_GetVersionForCurrentState isn't accurate for | ||
// custom dict subclasses at the moment. | ||
if (!PyDict_CheckExact(owner_dict)) { | ||
// _PyDictKeys_GetVersionForCurrentState isn't accurate for | ||
// custom dict subclasses at the moment | ||
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_DICT_SUBCLASS); | ||
goto fail; | ||
} | ||
|
@@ -867,9 +867,9 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, | |
} // else owner is maybe a builtin with no dict, or __slots__ | ||
|
||
// Borrowed. Check tp_version_tag before accessing in case it's deleted. | ||
cache2->meth = descr; | ||
cache1->attr.dk_version_or_hint = keys_version; | ||
cache1->attr.tp_version = owner_cls->tp_version_tag; | ||
cache2->obj = descr; | ||
cache1->dk_version_or_hint = keys_version; | ||
cache1->tp_version = owner_cls->tp_version_tag; | ||
*instr = _Py_MAKECODEUNIT(LOAD_METHOD_CACHED, _Py_OPARG(*instr)); | ||
goto success; | ||
|
||
|
Uh oh!
8000
There was an error while loading. Please reload this page.
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.
Is this safe?
I think it is, as long as all reads are guarded by type version checks, and the obj belongs to the class (or a super-class) being versioned checked, and they is no way for the state of the class (or any of its superclasses) to change between the check and the
Py_INCREF(obj)
.Could you add a (reasonably detailed) explanation of why this is safe, and a clear warning of what pre-conditions are necessary to keep it safe.
Uh oh!
There was an error while loading. Please reload this page.
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.
Yes. I thought quite hard about this before going ahead 😉 .
In the case of methods, there will always be a reference to
obj
held by the class so it will never be cleared by GC. Should the method be deleted, e.g.del X.y
, then the type cache will be invalidated and its version tag updated (this even works for super classes). We only need to check the type version tag.The preexisting type method cache (
MCACHE
) uses the exact same principles -- it's a 4096(?) element array of borrowed descriptor objects! We can make use of all the heavy lifting it's already done for us in this area --tp_version_tag
is always invalidated if any class in its MRO is modified, and on everytype_set_xx
operation (seePyType_Modified
usages intypeobject.c
). So if it somehow lasted from 2.6 till present, I'd think it's battle-tested enough.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.
Added at the specialization site instead of the struct declaration:
afcf51e#diff-22e653d878778ca28317261f7826545288d02fd9980cf485d0329f28a34993f2R871