mirror of
https://github.com/python/cpython.git
synced 2025-09-04 07:51:13 +00:00
bpo-44826: Specialize STORE_ATTR (GH-27590)
* Generalize cache names for LOAD_ATTR to allow store and delete specializations. * Factor out specialization of attribute dictionary access. * Specialize STORE_ATTR.
This commit is contained in:
parent
b854557b49
commit
ac75f6bdd4
9 changed files with 345 additions and 93 deletions
125
Python/ceval.c
125
Python/ceval.c
|
@ -2766,6 +2766,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
}
|
||||
|
||||
case TARGET(STORE_ATTR): {
|
||||
PREDICTED(STORE_ATTR);
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *v = SECOND();
|
||||
|
@ -3394,7 +3395,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyLoadAttrCache *cache1 = &caches[-1].load_attr;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, LOAD_ATTR);
|
||||
assert(tp->tp_dictoffset > 0);
|
||||
|
@ -3418,7 +3419,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
PyObject *res;
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyLoadAttrCache *cache1 = &caches[-1].load_attr;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
|
||||
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
|
||||
assert(dict != NULL);
|
||||
|
@ -3443,7 +3444,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyLoadAttrCache *cache1 = &caches[-1].load_attr;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, LOAD_ATTR);
|
||||
assert(tp->tp_dictoffset > 0);
|
||||
|
@ -3472,7 +3473,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyLoadAttrCache *cache1 = &caches[-1].load_attr;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, LOAD_ATTR);
|
||||
char *addr = (char *)owner + cache0->index;
|
||||
|
@ -3486,6 +3487,121 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(STORE_ATTR_ADAPTIVE): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
SpecializedCacheEntry *cache = GET_CACHE();
|
||||
if (cache->adaptive.counter == 0) {
|
||||
PyObject *owner = TOP();
|
||||
PyObject *name = GETITEM(names, cache->adaptive.original_oparg);
|
||||
next_instr--;
|
||||
if (_Py_Specialize_StoreAttr(owner, next_instr, name, cache) < 0) {
|
||||
goto error;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
else {
|
||||
STAT_INC(STORE_ATTR, deferred);
|
||||
cache->adaptive.counter--;
|
||||
oparg = cache->adaptive.original_oparg;
|
||||
JUMP_TO_INSTRUCTION(STORE_ATTR);
|
||||
}
|
||||
}
|
||||
|
||||
case TARGET(STORE_ATTR_SPLIT_KEYS): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, STORE_ATTR);
|
||||
assert(tp->tp_dictoffset > 0);
|
||||
PyDictObject *dict = *(PyDictObject **)(((char *)owner) + tp->tp_dictoffset);
|
||||
DEOPT_IF(dict == NULL, STORE_ATTR);
|
||||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, STORE_ATTR);
|
||||
/* Need to maintain ordering of dicts */
|
||||
DEOPT_IF(cache0->index > 0 && dict->ma_values[cache0->index-1] == NULL, STORE_ATTR);
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
record_cache_hit(cache0);
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyObject *old_value = dict->ma_values[cache0->index];
|
||||
dict->ma_values[cache0->index] = value;
|
||||
if (old_value == NULL) {
|
||||
dict->ma_used++;
|
||||
}
|
||||
else {
|
||||
Py_DECREF(old_value);
|
||||
}
|
||||
/* Ensure dict is GC tracked if it needs to be */
|
||||
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) {
|
||||
_PyObject_GC_TRACK(dict);
|
||||
}
|
||||
/* PEP 509 */
|
||||
dict->ma_version_tag = DICT_NEXT_VERSION();
|
||||
Py_DECREF(owner);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(STORE_ATTR_WITH_HINT): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, STORE_ATTR);
|
||||
assert(tp->tp_dictoffset > 0);
|
||||
PyDictObject *dict = *(PyDictObject **)(((char *)owner) + tp->tp_dictoffset);
|
||||
DEOPT_IF(dict == NULL, STORE_ATTR);
|
||||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
PyObject *name = GETITEM(names, cache0->original_oparg);
|
||||
uint32_t hint = cache1->dk_version_or_hint;
|
||||
DEOPT_IF(hint >= dict->ma_keys->dk_nentries, STORE_ATTR);
|
||||
PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
PyObject *old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
record_cache_hit(cache0);
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
ep->me_value = value;
|
||||
Py_DECREF(old_value);
|
||||
/* Ensure dict is GC tracked if it needs to be */
|
||||
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) {
|
||||
_PyObject_GC_TRACK(dict);
|
||||
}
|
||||
/* PEP 509 */
|
||||
dict->ma_version_tag = DICT_NEXT_VERSION();
|
||||
Py_DECREF(owner);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(STORE_ATTR_SLOT): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
assert(cache1->tp_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != cache1->tp_version, STORE_ATTR);
|
||||
char *addr = (char *)owner + cache0->index;
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
record_cache_hit(cache0);
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyObject *old_value = *(PyObject **)addr;
|
||||
*(PyObject **)addr = value;
|
||||
Py_XDECREF(old_value);
|
||||
Py_DECREF(owner);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(COMPARE_OP): {
|
||||
assert(oparg <= Py_GE);
|
||||
PyObject *right = POP();
|
||||
|
@ -4429,6 +4545,7 @@ opname ## _miss: \
|
|||
}
|
||||
|
||||
MISS_WITH_CACHE(LOAD_ATTR)
|
||||
MISS_WITH_CACHE(STORE_ATTR)
|
||||
MISS_WITH_CACHE(LOAD_GLOBAL)
|
||||
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue