#endif
#include "pycore_dict_state.h"
+#include "pycore_object.h"
#include "pycore_runtime.h" // _PyRuntime
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
extern size_t _PyDict_KeysSize(PyDictKeysObject *keys);
+extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
+
/* _Py_dict_lookup() returns index of entry which can be used like DK_ENTRIES(dk)[index].
* -1 when no entry found, -3 when compare raises error.
*/
}
extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values);
+extern int _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv);
extern PyObject *_PyDict_FromItems(
PyObject *const *keys, Py_ssize_t keys_offset,
PyObject *const *values, Py_ssize_t values_offset,
uint64_t dict_materialized_new_key;
uint64_t dict_materialized_too_big;
uint64_t dict_materialized_str_subclass;
+ uint64_t dict_dematerialized;
uint64_t type_cache_hits;
uint64_t type_cache_misses;
uint64_t type_cache_dunder_hits;
items = []
for _ in range(self.ITEMS):
item = C()
- item.__dict__
item.a = None
+ # Resize into a combined unicode dict:
+ for i in range(29):
+ setattr(item, f"_{i}", None)
items.append(item)
return items
items = []
for _ in range(self.ITEMS):
item = C()
- item.__dict__
+ # Resize into a combined unicode dict:
+ for i in range(29):
+ setattr(item, f"_{i}", None)
items.append(item)
return items
--- /dev/null
+Reduce the number of materialized instances dictionaries by dematerializing
+them when possible.
return make_dict_from_instance_attributes(interp, keys, values);
}
+// Return 1 if the dict was dematerialized, 0 otherwise.
+int
+_PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv)
+{
+ assert(_PyObject_DictOrValuesPointer(obj) == dorv);
+ assert(!_PyDictOrValues_IsValues(*dorv));
+ PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv);
+ if (dict == NULL) {
+ return 0;
+ }
+ // It's likely that this dict still shares its keys (if it was materialized
+ // on request and not heavily modified):
+ assert(PyDict_CheckExact(dict));
+ assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE));
+ if (dict->ma_keys != CACHED_KEYS(Py_TYPE(obj)) || Py_REFCNT(dict) != 1) {
+ return 0;
+ }
+ assert(dict->ma_values);
+ // We have an opportunity to do something *really* cool: dematerialize it!
+ _PyDictKeys_DecRef(dict->ma_keys);
+ _PyDictOrValues_SetValues(dorv, dict->ma_values);
+ OBJECT_STAT_INC(dict_dematerialized);
+ // Don't try this at home, kids:
+ dict->ma_keys = NULL;
+ dict->ma_values = NULL;
+ Py_DECREF(dict);
+ return 1;
+}
+
int
_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
PyObject *name, PyObject *value)
dict = _PyDictOrValues_GetDict(*dorv_ptr);
if (dict == NULL) {
dictkeys_incref(CACHED_KEYS(tp));
+ OBJECT_STAT_INC(dict_materialized_on_request);
dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp));
dorv_ptr->dict = dict;
}
dict = *dictptr;
if (dict == NULL) {
dictkeys_incref(cached);
+ if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
+ OBJECT_STAT_INC(dict_materialized_on_request);
+ }
dict = new_dict_with_shared_keys(interp, cached);
if (dict == NULL)
return -1;
return res;
}
-extern void
-_PyDictKeys_DecRef(PyDictKeysObject *keys);
-
static void
type_dealloc_common(PyTypeObject *type)
op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) {
assert(Py_TYPE(owner)->tp_dictoffset < 0);
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
}
op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) {
assert(type_version != 0);
DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR);
assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls;
DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version !=
keys_version, LOAD_ATTR);
assert(type_version != 0);
DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR);
assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls;
DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version !=
keys_version, LOAD_ATTR);
owner = stack_pointer[-1];
assert(Py_TYPE(owner)->tp_dictoffset < 0);
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
break;
}
{
assert(Py_TYPE(owner)->tp_dictoffset < 0);
assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
}
// _LOAD_ATTR_INSTANCE_VALUE
{
assert(type_version != 0);
DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR);
assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls;
DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version !=
keys_version, LOAD_ATTR);
assert(type_version != 0);
DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR);
assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv),
+ LOAD_ATTR);
PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls;
DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version !=
keys_version, LOAD_ATTR);
fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key);
fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big);
fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass);
+ fprintf(out, "Object dematerialize dict: %" PRIu64 "\n", stats->dict_dematerialized);
fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits);
fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses);
fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions);
return 0;
}
_PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- if (_PyDictOrValues_IsValues(dorv)) {
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
+ if (_PyDictOrValues_IsValues(*dorv) ||
+ _PyObject_MakeInstanceAttributesFromDict(owner, dorv))
+ {
// Virtual dictionary
PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys;
assert(PyUnicode_CheckExact(name));
instr->op.code = values_op;
}
else {
- PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
+ PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv);
if (dict == NULL || !PyDict_CheckExact(dict)) {
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT);
return 0;
}
// We found an instance with a __dict__.
+ if (dict->ma_values) {
+ SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NON_STRING_OR_SPLIT);
+ return 0;
+ }
Py_ssize_t index =
_PyDict_LookupIndex(dict, name);
if (index != (uint16_t)index) {
assert(descr != NULL);
assert((is_method && kind == METHOD) || (!is_method && kind == NON_DESCRIPTOR));
if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
+ PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner);
PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys;
- if (!_PyDictOrValues_IsValues(dorv)) {
+ if (!_PyDictOrValues_IsValues(*dorv) &&
+ !_PyObject_MakeInstanceAttributesFromDict(owner, dorv))
+ {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT);
return 0;
}