diff options
author | Mark Shannon <mark@hotpy.org> | 2021-10-13 13:19:34 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-13 13:19:34 (GMT) |
commit | a8b9350964f43cb648c98c179c8037fbf3ff8a7d (patch) | |
tree | 13a539432c9d48ac278d34d040f17a7a12eac771 /Python/ceval.c | |
parent | 97308dfcdc0696e0b116c37386e2ff4d72e6c3f4 (diff) | |
download | cpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.zip cpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.tar.gz cpython-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.tar.bz2 |
bpo-45340: Don't create object dictionaries unless actually needed (GH-28802)
* Never change types' cached keys. It could invalidate inline attribute objects.
* Lazily create object dictionaries.
* Update specialization of LOAD/STORE_ATTR.
* Don't update shared keys version for deletion of value.
* Update gdb support to handle instance values.
* Rename SPLIT_KEYS opcodes to INSTANCE_VALUE.
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 72 |
1 files changed, 36 insertions, 36 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index f4cacd8..5cf2ab3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3618,7 +3618,7 @@ check_eval_breaker: } } - TARGET(LOAD_ATTR_SPLIT_KEYS) { + TARGET(LOAD_ATTR_INSTANCE_VALUE) { assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; @@ -3629,11 +3629,10 @@ check_eval_breaker: assert(cache1->tp_version != 0); DEOPT_IF(tp->tp_version_tag != cache1->tp_version, LOAD_ATTR); assert(tp->tp_dictoffset > 0); - PyDictObject *dict = *(PyDictObject **)(((char *)owner) + tp->tp_dictoffset); - DEOPT_IF(dict == NULL, LOAD_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); - DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR); - res = dict->ma_values->values[cache0->index]; + assert(tp->tp_inline_values_offset > 0); + PyDictValues *values = *(PyDictValues **)(((char *)owner) + tp->tp_inline_values_offset); + DEOPT_IF(values == NULL, LOAD_ATTR); + res = values->values[cache0->index]; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); record_cache_hit(cache0); @@ -3725,7 +3724,7 @@ check_eval_breaker: } } - TARGET(STORE_ATTR_SPLIT_KEYS) { + TARGET(STORE_ATTR_INSTANCE_VALUE) { assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); @@ -3735,31 +3734,23 @@ check_eval_breaker: 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); + assert(tp->tp_inline_values_offset > 0); + PyDictValues *values = *(PyDictValues **)(((char *)owner) + tp->tp_inline_values_offset); + DEOPT_IF(values == NULL, STORE_ATTR); STAT_INC(STORE_ATTR, hit); record_cache_hit(cache0); int index = cache0->index; STACK_SHRINK(1); PyObject *value = POP(); - PyObject *old_value = dict->ma_values->values[index]; - dict->ma_values->values[index] = value; + PyObject *old_value = values->values[index]; + values->values[index] = value; if (old_value == NULL) { assert(index < 16); - dict->ma_values->mv_order = (dict->ma_values->mv_order << 4) | index; - dict->ma_used++; + values->mv_order = (values->mv_order << 4) | index; } 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(); } @@ -4474,21 +4465,31 @@ check_eval_breaker: _PyObjectCache *cache2 = &caches[-2].obj; DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD); - assert(cache1->dk_version_or_hint != 0); - assert(cache1->tp_version != 0); - assert(self_cls->tp_dictoffset >= 0); - assert(Py_TYPE(self_cls)->tp_dictoffset > 0); - - // inline version of _PyObject_GetDictPtr for offset >= 0 - PyObject *dict = self_cls->tp_dictoffset != 0 ? - *(PyObject **) ((char *)self + self_cls->tp_dictoffset) : NULL; - - // Ensure self.__dict__ didn't modify keys. - // Don't care if self has no dict, it could be builtin or __slots__. - DEOPT_IF(dict != NULL && - ((PyDictObject *)dict)->ma_keys->dk_version != - cache1->dk_version_or_hint, LOAD_METHOD); + assert(self_cls->tp_dictoffset > 0); + assert(self_cls->tp_inline_values_offset > 0); + PyDictObject *dict = *(PyDictObject **)(((char *)self) + self_cls->tp_dictoffset); + DEOPT_IF(dict != NULL, LOAD_METHOD); + DEOPT_IF(((PyHeapTypeObject *)self_cls)->ht_cached_keys->dk_version != cache1->dk_version_or_hint, LOAD_METHOD); + STAT_INC(LOAD_METHOD, hit); + record_cache_hit(cache0); + PyObject *res = cache2->obj; + assert(res != NULL); + assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); + Py_INCREF(res); + SET_TOP(res); + PUSH(self); + DISPATCH(); + } + TARGET(LOAD_METHOD_NO_DICT) { + PyObject *self = TOP(); + PyTypeObject *self_cls = Py_TYPE(self); + SpecializedCacheEntry *caches = GET_CACHE(); + _PyAdaptiveEntry *cache0 = &caches[0].adaptive; + _PyAttrCache *cache1 = &caches[-1].attr; + _PyObjectCache *cache2 = &caches[-2].obj; + DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD); + assert(self_cls->tp_dictoffset == 0); STAT_INC(LOAD_METHOD, hit); record_cache_hit(cache0); PyObject *res = cache2->obj; @@ -4530,7 +4531,6 @@ check_eval_breaker: record_cache_hit(cache0); PyObject *res = cache2->obj; assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); Py_INCREF(res); SET_TOP(NULL); Py_DECREF(cls); |