| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,6 +10,7 @@ extern "C" { | |||
| 10 | 10 | #endif | |
| 11 | 11 | ||
| 12 | 12 | #include "pycore_dict_state.h" | |
| 13 | + #include "pycore_object.h" | ||
| 13 | 14 | #include "pycore_runtime.h" // _PyRuntime | |
| 14 | 15 | ||
| 15 | 16 | // Unsafe flavor of PyDict_GetItemWithError(): no error checking | |
@@ -62,6 +63,8 @@ extern uint32_t _PyDictKeys_GetVersionForCurrentState( | |||
| 62 | 63 | ||
| 63 | 64 | extern size_t _PyDict_KeysSize(PyDictKeysObject *keys); | |
| 64 | 65 | ||
| 66 | + extern void _PyDictKeys_DecRef(PyDictKeysObject *keys); | ||
| 67 | + | ||
| 65 | 68 | /* _Py_dict_lookup() returns index of entry which can be used like DK_ENTRIES(dk)[index]. | |
| 66 | 69 | * -1 when no entry found, -3 when compare raises error. | |
| 67 | 70 | */ | |
@@ -196,6 +199,7 @@ _PyDict_NotifyEvent(PyInterpreterState *interp, | |||
| 196 | 199 | } | |
| 197 | 200 | ||
| 198 | 201 | extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values); | |
| 202 | + extern int _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv); | ||
| 199 | 203 | extern PyObject *_PyDict_FromItems( | |
| 200 | 204 | PyObject *const *keys, Py_ssize_t keys_offset, | |
| 201 | 205 | PyObject *const *values, Py_ssize_t values_offset, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -65,6 +65,7 @@ typedef struct _object_stats { | |||
| 65 | 65 | uint64_t dict_materialized_new_key; | |
| 66 | 66 | uint64_t dict_materialized_too_big; | |
| 67 | 67 | uint64_t dict_materialized_str_subclass; | |
| 68 | + uint64_t dict_dematerialized; | ||
| 68 | 69 | uint64_t type_cache_hits; | |
| 69 | 70 | uint64_t type_cache_misses; | |
| 70 | 71 | uint64_t type_cache_dunder_hits; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -865,8 +865,10 @@ class C: | |||
| 865 | 865 | items = [] | |
| 866 | 866 | for _ in range(self.ITEMS): | |
| 867 | 867 | item = C() | |
| 868 | - item.__dict__ | ||
| 869 | 868 | item.a = None | |
| 869 | + # Resize into a combined unicode dict: | ||
| 870 | + for i in range(29): | ||
| 871 | + setattr(item, f"_{i}", None) | ||
| 870 | 872 | items.append(item) | |
| 871 | 873 | return items | |
| 872 | 874 | ||
@@ -932,7 +934,9 @@ class C: | |||
| 932 | 934 | items = [] | |
| 933 | 935 | for _ in range(self.ITEMS): | |
| 934 | 936 | item = C() | |
| 935 | - item.__dict__ | ||
| 937 | + # Resize into a combined unicode dict: | ||
| 938 | + for i in range(29): | ||
| 939 | + setattr(item, f"_{i}", None) | ||
| 936 | 940 | items.append(item) | |
| 937 | 941 | return items | |
| 938 | 942 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Reduce the number of materialized instances dictionaries by dematerializing | ||
| 2 | + them when possible. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5464,6 +5464,35 @@ _PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values) | |||
| 5464 | 5464 | return make_dict_from_instance_attributes(interp, keys, values); | |
| 5465 | 5465 | } | |
| 5466 | 5466 | ||
| 5467 | + // Return 1 if the dict was dematerialized, 0 otherwise. | ||
| 5468 | + int | ||
| 5469 | + _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv) | ||
| 5470 | + { | ||
| 5471 | + assert(_PyObject_DictOrValuesPointer(obj) == dorv); | ||
| 5472 | + assert(!_PyDictOrValues_IsValues(*dorv)); | ||
| 5473 | + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv); | ||
| 5474 | + if (dict == NULL) { | ||
| 5475 | + return 0; | ||
| 5476 | + } | ||
| 5477 | + // It's likely that this dict still shares its keys (if it was materialized | ||
| 5478 | + // on request and not heavily modified): | ||
| 5479 | + assert(PyDict_CheckExact(dict)); | ||
| 5480 | + assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE)); | ||
| 5481 | + if (dict->ma_keys != CACHED_KEYS(Py_TYPE(obj)) || Py_REFCNT(dict) != 1) { | ||
| 5482 | + return 0; | ||
| 5483 | + } | ||
| 5484 | + assert(dict->ma_values); | ||
| 5485 | + // We have an opportunity to do something *really* cool: dematerialize it! | ||
| 5486 | + _PyDictKeys_DecRef(dict->ma_keys); | ||
| 5487 | + _PyDictOrValues_SetValues(dorv, dict->ma_values); | ||
| 5488 | + OBJECT_STAT_INC(dict_dematerialized); | ||
| 5489 | + // Don't try this at home, kids: | ||
| 5490 | + dict->ma_keys = NULL; | ||
| 5491 | + dict->ma_values = NULL; | ||
| 5492 | + Py_DECREF(dict); | ||
| 5493 | + return 1; | ||
| 5494 | + } | ||
| 5495 | + | ||
| 5467 | 5496 | int | |
| 5468 | 5497 | _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, | |
| 5469 | 5498 | PyObject *name, PyObject *value) | |
@@ -5688,6 +5717,7 @@ PyObject_GenericGetDict(PyObject *obj, void *context) | |||
| 5688 | 5717 | dict = _PyDictOrValues_GetDict(*dorv_ptr); | |
| 5689 | 5718 | if (dict == NULL) { | |
| 5690 | 5719 | dictkeys_incref(CACHED_KEYS(tp)); | |
| 5720 | + OBJECT_STAT_INC(dict_materialized_on_request); | ||
| 5691 | 5721 | dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); | |
| 5692 | 5722 | dorv_ptr->dict = dict; | |
| 5693 | 5723 | } | |
@@ -5731,6 +5761,9 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, | |||
| 5731 | 5761 | dict = *dictptr; | |
| 5732 | 5762 | if (dict == NULL) { | |
| 5733 | 5763 | dictkeys_incref(cached); | |
| 5764 | + if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) { | ||
| 5765 | + OBJECT_STAT_INC(dict_materialized_on_request); | ||
| 5766 | + } | ||
| 5734 | 5767 | dict = new_dict_with_shared_keys(interp, cached); | |
| 5735 | 5768 | if (dict == NULL) | |
| 5736 | 5769 | return -1; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4965,9 +4965,6 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) | |||
| 4965 | 4965 | return res; | |
| 4966 | 4966 | } | |
| 4967 | 4967 | ||
| 4968 | - extern void | ||
| 4969 | - _PyDictKeys_DecRef(PyDictKeysObject *keys); | ||
| 4970 | - | ||
| 4971 | 4968 | ||
| 4972 | 4969 | static void | |
| 4973 | 4970 | type_dealloc_common(PyTypeObject *type) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1827,8 +1827,10 @@ dummy_func( | |||
| 1827 | 1827 | op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { | |
| 1828 | 1828 | assert(Py_TYPE(owner)->tp_dictoffset < 0); | |
| 1829 | 1829 | assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); | |
| 1830 | - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); | ||
| 1831 | - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); | ||
| 1830 | + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); | ||
| 1831 | + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && | ||
| 1832 | + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), | ||
| 1833 | + LOAD_ATTR); | ||
| 1832 | 1834 | } | |
| 1833 | 1835 | ||
| 1834 | 1836 | op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) { | |
@@ -2727,8 +2729,10 @@ dummy_func( | |||
| 2727 | 2729 | assert(type_version != 0); | |
| 2728 | 2730 | DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); | |
| 2729 | 2731 | assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); | |
| 2730 | - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); | ||
| 2731 | - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); | ||
| 2732 | + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); | ||
| 2733 | + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && | ||
| 2734 | + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), | ||
| 2735 | + LOAD_ATTR); | ||
| 2732 | 2736 | PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; | |
| 2733 | 2737 | DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != | |
| 2734 | 2738 | keys_version, LOAD_ATTR); | |
@@ -2757,8 +2761,10 @@ dummy_func( | |||
| 2757 | 2761 | assert(type_version != 0); | |
| 2758 | 2762 | DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); | |
| 2759 | 2763 | assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); | |
| 2760 | - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); | ||
| 2761 | - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); | ||
| 2764 | + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); | ||
| 2765 | + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && | ||
| 2766 | + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), | ||
| 2767 | + LOAD_ATTR); | ||
| 2762 | 2768 | PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; | |
| 2763 | 2769 | DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != | |
| 2764 | 2770 | keys_version, LOAD_ATTR); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -192,6 +192,7 @@ print_object_stats(FILE *out, ObjectStats *stats) | |||
| 192 | 192 | fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key); | |
| 193 | 193 | fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big); | |
| 194 | 194 | fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass); | |
| 195 | + fprintf(out, "Object dematerialize dict: %" PRIu64 "\n", stats->dict_dematerialized); | ||
| 195 | 196 | fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits); | |
| 196 | 197 | fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses); | |
| 197 | 198 | fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions); | |
@@ -685,8 +686,10 @@ specialize_dict_access( | |||
| 685 | 686 | return 0; | |
| 686 | 687 | } | |
| 687 | 688 | _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); | |
| 688 | - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); | ||
| 689 | - if (_PyDictOrValues_IsValues(dorv)) { | ||
| 689 | + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); | ||
| 690 | + if (_PyDictOrValues_IsValues(*dorv) || | ||
| 691 | + _PyObject_MakeInstanceAttributesFromDict(owner, dorv)) | ||
| 692 | + { | ||
| 690 | 693 | // Virtual dictionary | |
| 691 | 694 | PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; | |
| 692 | 695 | assert(PyUnicode_CheckExact(name)); | |
@@ -704,12 +707,16 @@ specialize_dict_access( | |||
| 704 | 707 | instr->op.code = values_op; | |
| 705 | 708 | } | |
| 706 | 709 | else { | |
| 707 | - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); | ||
| 710 | + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv); | ||
| 708 | 711 | if (dict == NULL || !PyDict_CheckExact(dict)) { | |
| 709 | 712 | SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT); | |
| 710 | 713 | return 0; | |
| 711 | 714 | } | |
| 712 | 715 | // We found an instance with a __dict__. | |
| 716 | + if (dict->ma_values) { | ||
| 717 | + SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NON_STRING_OR_SPLIT); | ||
| 718 | + return 0; | ||
| 719 | + } | ||
| 713 | 720 | Py_ssize_t index = | |
| 714 | 721 | _PyDict_LookupIndex(dict, name); | |
| 715 | 722 | if (index != (uint16_t)index) { | |
@@ -1100,9 +1107,11 @@ PyObject *descr, DescriptorClassification kind, bool is_method) | |||
| 1100 | 1107 | assert(descr != NULL); | |
| 1101 | 1108 | assert((is_method && kind == METHOD) || (!is_method && kind == NON_DESCRIPTOR)); | |
| 1102 | 1109 | if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | |
| 1103 | - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); | ||
| 1110 | + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); | ||
| 1104 | 1111 | PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; | |
| 1105 | - if (!_PyDictOrValues_IsValues(dorv)) { | ||
| 1112 | + if (!_PyDictOrValues_IsValues(*dorv) && | ||
| 1113 | + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) | ||
| 1114 | + { | ||
| 1106 | 1115 | SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); | |
| 1107 | 1116 | return 0; | |
| 1108 | 1117 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments