From c32dc47aca6e8fac152699bc613e015c44ccdba9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 2 Apr 2024 11:59:21 +0100 Subject: [PATCH] GH-115776: Embed the values array into the object, for "normal" Python objects. (GH-116115) --- Include/cpython/pystats.h | 3 +- Include/internal/pycore_code.h | 5 +- Include/internal/pycore_dict.h | 64 ++- Include/internal/pycore_object.h | 48 +- Include/internal/pycore_opcode_metadata.h | 6 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 10 +- Include/object.h | 7 +- Lib/test/test_capi/test_mem.py | 4 +- Lib/test/test_class.py | 77 ++- Lib/test/test_opcache.py | 13 +- ...-02-24-03-39-09.gh-issue-115776.THJXqg.rst | 4 + Modules/_testbuffer.c | 3 + Modules/_testcapimodule.c | 4 +- Modules/_testinternalcapi.c | 20 +- Objects/dictobject.c | 464 +++++++++--------- Objects/object.c | 100 ++-- Objects/object_layout.md | 91 +++- Objects/object_layout_312.gv | 1 + Objects/object_layout_312.png | Bin 30688 -> 33040 bytes Objects/object_layout_313.gv | 45 ++ Objects/object_layout_313.png | Bin 0 -> 35055 bytes Objects/object_layout_full_313.gv | 25 + Objects/object_layout_full_313.png | Bin 0 -> 16983 bytes Objects/typeobject.c | 84 ++-- Python/bytecodes.c | 52 +- Python/executor_cases.c.h | 44 +- Python/gc.c | 7 +- Python/gc_free_threading.c | 6 +- Python/generated_cases.c.h | 54 +- Python/optimizer_cases.c.h | 2 +- Python/specialize.c | 51 +- Tools/cases_generator/analyzer.py | 7 +- Tools/gdb/libpython.py | 14 +- Tools/scripts/summarize_stats.py | 5 +- 35 files changed, 786 insertions(+), 536 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst create mode 100644 Objects/object_layout_313.gv create mode 100644 Objects/object_layout_313.png create mode 100644 Objects/object_layout_full_313.gv create mode 100644 Objects/object_layout_full_313.png diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index 2fb7723f583c..e74fdd4d32e2 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -77,12 +77,11 @@ typedef struct _object_stats { uint64_t frees; uint64_t to_freelist; uint64_t from_freelist; - uint64_t new_values; + uint64_t inline_values; uint64_t dict_materialized_on_request; 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; diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index e004783ee481..6c90c9e28410 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -79,7 +79,10 @@ typedef struct { typedef struct { uint16_t counter; uint16_t type_version[2]; - uint16_t keys_version[2]; + union { + uint16_t keys_version[2]; + uint16_t dict_offset; + }; uint16_t descr[4]; } _PyLoadMethodCache; diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index ef59960dbab0..5507bdd539cc 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -11,7 +11,7 @@ extern "C" { #include "pycore_freelist.h" // _PyFreeListState #include "pycore_identifier.h" // _Py_Identifier -#include "pycore_object.h" // PyDictOrValues +#include "pycore_object.h" // PyManagedDictPointer // Unsafe flavor of PyDict_GetItemWithError(): no error checking extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key); @@ -181,6 +181,10 @@ struct _dictkeysobject { * [-1] = prefix size. [-2] = used size. size[-2-n...] = insertion order. */ struct _dictvalues { + uint8_t capacity; + uint8_t size; + uint8_t embedded; + uint8_t valid; PyObject *values[1]; }; @@ -196,6 +200,7 @@ static inline void* _DK_ENTRIES(PyDictKeysObject *dk) { size_t index = (size_t)1 << dk->dk_log2_index_bytes; return (&indices[index]); } + static inline PyDictKeyEntry* DK_ENTRIES(PyDictKeysObject *dk) { assert(dk->dk_kind == DICT_KEYS_GENERAL); return (PyDictKeyEntry*)_DK_ENTRIES(dk); @@ -211,9 +216,6 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) { #define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1) #define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1) -#define DICT_VALUES_SIZE(values) ((uint8_t *)values)[-1] -#define DICT_VALUES_USED_SIZE(values) ((uint8_t *)values)[-2] - #ifdef Py_GIL_DISABLED #define DICT_NEXT_VERSION(INTERP) \ (_Py_atomic_add_uint64(&(INTERP)->dict_state.global_version, DICT_VERSION_INCREMENT) + DICT_VERSION_INCREMENT) @@ -246,25 +248,63 @@ _PyDict_NotifyEvent(PyInterpreterState *interp, return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK); } -extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values); -PyAPI_FUNC(bool) _PyObject_MakeInstanceAttributesFromDict(PyObject *obj, PyDictOrValues *dorv); +extern PyDictObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj); + PyAPI_FUNC(PyObject *)_PyDict_FromItems( PyObject *const *keys, Py_ssize_t keys_offset, PyObject *const *values, Py_ssize_t values_offset, Py_ssize_t length); +static inline uint8_t * +get_insertion_order_array(PyDictValues *values) +{ + return (uint8_t *)&values->values[values->capacity]; +} + static inline void _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix) { assert(ix < SHARED_KEYS_MAX_SIZE); - uint8_t *size_ptr = ((uint8_t *)values)-2; - int size = *size_ptr; - assert(size+2 < DICT_VALUES_SIZE(values)); - size++; - size_ptr[-size] = (uint8_t)ix; - *size_ptr = size; + int size = values->size; + uint8_t *array = get_insertion_order_array(values); + assert(size < values->capacity); + assert(((uint8_t)ix) == ix); + array[size] = (uint8_t)ix; + values->size = size+1; +} + +static inline size_t +shared_keys_usable_size(PyDictKeysObject *keys) +{ +#ifdef Py_GIL_DISABLED + // dk_usable will decrease for each instance that is created and each + // value that is added. dk_nentries will increase for each value that + // is added. We want to always return the right value or larger. + // We therefore increase dk_nentries first and we decrease dk_usable + // second, and conversely here we read dk_usable first and dk_entries + // second (to avoid the case where we read entries before the increment + // and read usable after the decrement) + return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) + + _Py_atomic_load_ssize_acquire(&keys->dk_nentries)); +#else + return (size_t)keys->dk_nentries + (size_t)keys->dk_usable; +#endif } +static inline size_t +_PyInlineValuesSize(PyTypeObject *tp) +{ + PyDictKeysObject *keys = ((PyHeapTypeObject*)tp)->ht_cached_keys; + assert(keys != NULL); + size_t size = shared_keys_usable_size(keys); + size_t prefix_size = _Py_SIZE_ROUND_UP(size, sizeof(PyObject *)); + assert(prefix_size < 256); + return prefix_size + (size + 1) * sizeof(PyObject *); +} + +int +_PyDict_DetachFromObject(PyDictObject *dict, PyObject *obj); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 0b17ddf0c973..4fc5e9bf653c 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -265,9 +265,8 @@ _PyObject_Init(PyObject *op, PyTypeObject *typeobj) { assert(op != NULL); Py_SET_TYPE(op, typeobj); - if (_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE)) { - Py_INCREF(typeobj); - } + assert(_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE) || _Py_IsImmortal(typeobj)); + Py_INCREF(typeobj); _Py_NewReference(op); } @@ -611,8 +610,7 @@ extern PyTypeObject* _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *); extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int); -extern int _PyObject_InitializeDict(PyObject *obj); -int _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp); +void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp); extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, PyObject *name, PyObject *value); PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, @@ -627,46 +625,26 @@ PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values, #endif typedef union { - PyObject *dict; - /* Use a char* to generate a warning if directly assigning a PyDictValues */ - char *values; -} PyDictOrValues; + PyDictObject *dict; +} PyManagedDictPointer; -static inline PyDictOrValues * -_PyObject_DictOrValuesPointer(PyObject *obj) +static inline PyManagedDictPointer * +_PyObject_ManagedDictPointer(PyObject *obj) { assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - return (PyDictOrValues *)((char *)obj + MANAGED_DICT_OFFSET); -} - -static inline int -_PyDictOrValues_IsValues(PyDictOrValues dorv) -{ - return ((uintptr_t)dorv.values) & 1; + return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET); } static inline PyDictValues * -_PyDictOrValues_GetValues(PyDictOrValues dorv) -{ - assert(_PyDictOrValues_IsValues(dorv)); - return (PyDictValues *)(dorv.values + 1); -} - -static inline PyObject * -_PyDictOrValues_GetDict(PyDictOrValues dorv) +_PyObject_InlineValues(PyObject *obj) { - assert(!_PyDictOrValues_IsValues(dorv)); - return dorv.dict; -} - -static inline void -_PyDictOrValues_SetValues(PyDictOrValues *ptr, PyDictValues *values) -{ - ptr->values = ((char *)values) - 1; + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(Py_TYPE(obj)->tp_basicsize == sizeof(PyObject)); + return (PyDictValues *)((char *)obj + sizeof(PyObject)); } extern PyObject ** _PyObject_ComputedDictPointer(PyObject *); -extern void _PyObject_FreeInstanceAttributes(PyObject *obj); extern int _PyObject_IsInstanceDictEmpty(PyObject *); // Export for 'math' shared extension diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index de525f72d352..aa87dc413876 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1085,7 +1085,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, - [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, + [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1269,7 +1269,7 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, 0, 0 } } }, [LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } }, [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 0, 0 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } }, @@ -1316,7 +1316,7 @@ _PyOpcode_macro_expansion[256] = { [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { _SET_FUNCTION_ATTRIBUTE, 0, 0 } } }, [SET_UPDATE] = { .nuops = 1, .uops = { { _SET_UPDATE, 0, 0 } } }, [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } }, - [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, + [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_NO_DICT, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, 0, 0 } } }, [STORE_FAST] = { .nuops = 1, .uops = { { _STORE_FAST, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index bcb10ab723ec..54dc6dcf4081 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -109,7 +109,7 @@ extern "C" { #define _GUARD_BOTH_INT 347 #define _GUARD_BOTH_UNICODE 348 #define _GUARD_BUILTINS_VERSION 349 -#define _GUARD_DORV_VALUES 350 +#define _GUARD_DORV_NO_DICT 350 #define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 351 #define _GUARD_GLOBALS_VERSION 352 #define _GUARD_IS_FALSE_POP 353 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 51206cd4ca2f..0f2046fb3d0c 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -136,8 +136,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG | HAS_PASSTHROUGH_FLAG, - [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_0] = HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_1] = HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, @@ -145,7 +145,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_CLASS_0] = 0, [_LOAD_ATTR_CLASS_1] = 0, [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG | HAS_OPARG_AND_1_FLAG, - [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_DORV_NO_DICT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_STORE_ATTR_INSTANCE_VALUE] = 0, [_STORE_ATTR_SLOT] = HAS_ESCAPES_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -342,7 +342,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GUARD_BOTH_INT] = "_GUARD_BOTH_INT", [_GUARD_BOTH_UNICODE] = "_GUARD_BOTH_UNICODE", [_GUARD_BUILTINS_VERSION] = "_GUARD_BUILTINS_VERSION", - [_GUARD_DORV_VALUES] = "_GUARD_DORV_VALUES", + [_GUARD_DORV_NO_DICT] = "_GUARD_DORV_NO_DICT", [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = "_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT", [_GUARD_GLOBALS_VERSION] = "_GUARD_GLOBALS_VERSION", [_GUARD_IS_FALSE_POP] = "_GUARD_IS_FALSE_POP", @@ -736,7 +736,7 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _LOAD_ATTR_CLASS: return 1; - case _GUARD_DORV_VALUES: + case _GUARD_DORV_NO_DICT: return 1; case _STORE_ATTR_INSTANCE_VALUE: return 2; diff --git a/Include/object.h b/Include/object.h index 96790844a7b9..13443329dfb5 100644 --- a/Include/object.h +++ b/Include/object.h @@ -629,13 +629,18 @@ given type object has a specified feature. /* Track types initialized using _PyStaticType_InitBuiltin(). */ #define _Py_TPFLAGS_STATIC_BUILTIN (1 << 1) +/* The values array is placed inline directly after the rest of + * the object. Implies Py_TPFLAGS_HAVE_GC. + */ +#define Py_TPFLAGS_INLINE_VALUES (1 << 2) + /* Placement of weakref pointers are managed by the VM, not by the type. * The VM will automatically set tp_weaklistoffset. */ #define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3) /* Placement of dict (and values) pointers are managed by the VM, not by the type. - * The VM will automatically set tp_dictoffset. + * The VM will automatically set tp_dictoffset. Implies Py_TPFLAGS_HAVE_GC. */ #define Py_TPFLAGS_MANAGED_DICT (1 << 4) diff --git a/Lib/test/test_capi/test_mem.py b/Lib/test/test_capi/test_mem.py index 04f17a9ec9e7..1958ecc0df4a 100644 --- a/Lib/test/test_capi/test_mem.py +++ b/Lib/test/test_capi/test_mem.py @@ -148,8 +148,8 @@ class PyMemDebugTests(unittest.TestCase): self.assertIn(b'MemoryError', out) *_, count = line.split(b' ') count = int(count) - self.assertLessEqual(count, i*5) - self.assertGreaterEqual(count, i*5-2) + self.assertLessEqual(count, i*10) + self.assertGreaterEqual(count, i*10-4) # Py_GIL_DISABLED requires mimalloc (not malloc) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 0cf06243dc8d..4c1814142736 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -2,7 +2,6 @@ import unittest - testmeths = [ # Binary operations @@ -789,5 +788,81 @@ class ClassTests(unittest.TestCase): self.assertEqual(calls, 100) +from _testinternalcapi import has_inline_values + +Py_TPFLAGS_MANAGED_DICT = (1 << 2) + +class Plain: + pass + + +class WithAttrs: + + def __init__(self): + self.a = 1 + self.b = 2 + self.c = 3 + self.d = 4 + + +class TestInlineValues(unittest.TestCase): + + def test_flags(self): + self.assertEqual(Plain.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + self.assertEqual(WithAttrs.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + + def test_has_inline_values(self): + c = Plain() + self.assertTrue(has_inline_values(c)) + del c.__dict__ + self.assertFalse(has_inline_values(c)) + + def test_instances(self): + self.assertTrue(has_inline_values(Plain())) + self.assertTrue(has_inline_values(WithAttrs())) + + def test_inspect_dict(self): + for cls in (Plain, WithAttrs): + c = cls() + c.__dict__ + self.assertTrue(has_inline_values(c)) + + def test_update_dict(self): + d = { "e": 5, "f": 6 } + for cls in (Plain, WithAttrs): + c = cls() + c.__dict__.update(d) + self.assertTrue(has_inline_values(c)) + + @staticmethod + def set_100(obj): + for i in range(100): + setattr(obj, f"a{i}", i) + + def check_100(self, obj): + for i in range(100): + self.assertEqual(getattr(obj, f"a{i}"), i) + + def test_many_attributes(self): + class C: pass + c = C() + self.assertTrue(has_inline_values(c)) + self.set_100(c) + self.assertFalse(has_inline_values(c)) + self.check_100(c) + c = C() + self.assertTrue(has_inline_values(c)) + + def test_many_attributes_with_dict(self): + class C: pass + c = C() + d = c.__dict__ + self.assertTrue(has_inline_values(c)) + self.set_100(c) + self.assertFalse(has_inline_values(c)) + self.check_100(c) + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 5fb4b815c95d..8829c9a6d882 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1044,20 +1044,13 @@ class TestInstanceDict(unittest.TestCase): c.a = 1 c.b = 2 c.__dict__ - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) + self.assertEqual(c.__dict__, {"a":1, "b": 2}) def test_dict_dematerialization(self): c = C() c.a = 1 c.b = 2 c.__dict__ - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) for _ in range(100): c.a self.assertEqual( @@ -1072,10 +1065,6 @@ class TestInstanceDict(unittest.TestCase): d = c.__dict__ for _ in range(100): c.a - self.assertIs( - _testinternalcapi.get_object_dict_values(c), - None - ) self.assertIs(c.__dict__, d) def test_dict_dematerialization_copy(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst new file mode 100644 index 000000000000..5974b1882acb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-24-03-39-09.gh-issue-115776.THJXqg.rst @@ -0,0 +1,4 @@ +The array of values, the ``PyDictValues`` struct is now embedded in the +object during allocation. This provides better performance in the common +case, and does not degrade as much when the object's ``__dict__`` is +materialized. diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c index 5084bcadb10f..cad21bdb4d85 100644 --- a/Modules/_testbuffer.c +++ b/Modules/_testbuffer.c @@ -2820,6 +2820,9 @@ static int _testbuffer_exec(PyObject *mod) { Py_SET_TYPE(&NDArray_Type, &PyType_Type); + if (PyType_Ready(&NDArray_Type)) { + return -1; + } if (PyModule_AddType(mod, &NDArray_Type) < 0) { return -1; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index e9db6e5683e3..b2af47d05ee1 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3869,7 +3869,9 @@ PyInit__testcapi(void) return NULL; Py_SET_TYPE(&_HashInheritanceTester_Type, &PyType_Type); - + if (PyType_Ready(&_HashInheritanceTester_Type) < 0) { + return NULL; + } if (PyType_Ready(&matmulType) < 0) return NULL; Py_INCREF(&matmulType); diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index c07652facc0a..d6d50e75b612 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -15,7 +15,7 @@ #include "pycore_ceval.h" // _PyEval_AddPendingCall() #include "pycore_compile.h" // _PyCompile_CodeGen() #include "pycore_context.h" // _PyContext_NewHamtForTests() -#include "pycore_dict.h" // _PyDictOrValues_GetValues() +#include "pycore_dict.h" // _PyManagedDictPointer_GetValues() #include "pycore_fileutils.h" // _Py_normpath() #include "pycore_frame.h" // _PyInterpreterFrame #include "pycore_gc.h" // PyGC_Head @@ -1297,14 +1297,13 @@ static PyObject * get_object_dict_values(PyObject *self, PyObject *obj) { PyTypeObject *type = Py_TYPE(obj); - if (!_PyType_HasFeature(type, Py_TPFLAGS_MANAGED_DICT)) { + if (!_PyType_HasFeature(type, Py_TPFLAGS_INLINE_VALUES)) { Py_RETURN_NONE; } - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (!_PyDictOrValues_IsValues(dorv)) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (!values->valid) { Py_RETURN_NONE; } - PyDictValues *values = _PyDictOrValues_GetValues(dorv); PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; assert(keys != NULL); int size = (int)keys->dk_nentries; @@ -1784,6 +1783,16 @@ get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) } #endif +static PyObject * +has_inline_values(PyObject *self, PyObject *obj) +{ + if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_InlineValues(obj)->valid) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1857,6 +1866,7 @@ static PyMethodDef module_functions[] = { _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF {"get_rare_event_counters", get_rare_event_counters, METH_NOARGS}, {"reset_rare_event_counters", reset_rare_event_counters, METH_NOARGS}, + {"has_inline_values", has_inline_values, METH_O}, #ifdef Py_GIL_DISABLED {"py_thread_id", get_py_thread_id, METH_NOARGS}, #endif diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 536746ca41ee..58a3d979339c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -361,6 +361,10 @@ static int dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result, int incref_result); +#ifndef NDEBUG +static int _PyObject_InlineValuesConsistencyCheck(PyObject *obj); +#endif + #include "clinic/dictobject.c.h" @@ -624,8 +628,9 @@ static inline int get_index_from_order(PyDictObject *mp, Py_ssize_t i) { assert(mp->ma_used <= SHARED_KEYS_MAX_SIZE); - assert(i < (((char *)mp->ma_values)[-2])); - return ((char *)mp->ma_values)[-3-i]; + assert(i < mp->ma_values->size); + uint8_t *array = get_insertion_order_array(mp->ma_values); + return array[i]; } #ifdef DEBUG_PYDICT @@ -672,6 +677,10 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) else { CHECK(keys->dk_kind == DICT_KEYS_SPLIT); CHECK(mp->ma_used <= SHARED_KEYS_MAX_SIZE); + if (mp->ma_values->embedded) { + CHECK(mp->ma_values->embedded == 1); + CHECK(mp->ma_values->valid == 1); + } } if (check_content) { @@ -821,33 +830,44 @@ free_keys_object(PyDictKeysObject *keys, bool use_qsbr) PyMem_Free(keys); } +static size_t +values_size_from_count(size_t count) +{ + assert(count >= 1); + size_t suffix_size = _Py_SIZE_ROUND_UP(count, sizeof(PyObject *)); + assert(suffix_size < 128); + assert(suffix_size % sizeof(PyObject *) == 0); + return (count + 1) * sizeof(PyObject *) + suffix_size; +} + +#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) + static inline PyDictValues* new_values(size_t size) { - assert(size >= 1); - size_t prefix_size = _Py_SIZE_ROUND_UP(size+2, sizeof(PyObject *)); - assert(prefix_size < 256); - size_t n = prefix_size + size * sizeof(PyObject *); - uint8_t *mem = PyMem_Malloc(n); - if (mem == NULL) { + size_t n = values_size_from_count(size); + PyDictValues *res = (PyDictValues *)PyMem_Malloc(n); + if (res == NULL) { return NULL; } - assert(prefix_size % sizeof(PyObject *) == 0); - mem[prefix_size-1] = (uint8_t)prefix_size; - return (PyDictValues*)(mem + prefix_size); + res->embedded = 0; + res->size = 0; + assert(size < 256); + res->capacity = (uint8_t)size; + return res; } static inline void free_values(PyDictValues *values, bool use_qsbr) { - int prefix_size = DICT_VALUES_SIZE(values); + assert(values->embedded == 0); #ifdef Py_GIL_DISABLED if (use_qsbr) { - _PyMem_FreeDelayed(((char *)values)-prefix_size); + _PyMem_FreeDelayed(values); return; } #endif - PyMem_Free(((char *)values)-prefix_size); + PyMem_Free(values); } /* Consumes a reference to the keys object */ @@ -887,24 +907,6 @@ new_dict(PyInterpreterState *interp, return (PyObject *)mp; } -static inline size_t -shared_keys_usable_size(PyDictKeysObject *keys) -{ -#ifdef Py_GIL_DISABLED - // dk_usable will decrease for each instance that is created and each - // value that is added. dk_nentries will increase for each value that - // is added. We want to always return the right value or larger. - // We therefore increase dk_nentries first and we decrease dk_usable - // second, and conversely here we read dk_usable first and dk_entries - // second (to avoid the case where we read entries before the increment - // and read usable after the decrement) - return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) + - _Py_atomic_load_ssize_acquire(&keys->dk_nentries)); -#else - return (size_t)keys->dk_nentries + (size_t)keys->dk_usable; -#endif -} - /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys) @@ -915,7 +917,6 @@ new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys) dictkeys_decref(interp, keys, false); return PyErr_NoMemory(); } - ((char *)values)[-2] = 0; for (size_t i = 0; i < size; i++) { values->values[i] = NULL; } @@ -1419,7 +1420,7 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb if (values == NULL) goto read_failed; - uint8_t capacity = _Py_atomic_load_uint8_relaxed(&DICT_VALUES_SIZE(values)); + uint8_t capacity = _Py_atomic_load_uint8_relaxed(&values->capacity); if (ix >= (Py_ssize_t)capacity) goto read_failed; @@ -1525,6 +1526,7 @@ _PyDict_MaybeUntrack(PyObject *op) return; mp = (PyDictObject *) op; + ASSERT_CONSISTENT(mp); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { @@ -1945,7 +1947,14 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp, set_keys(mp, newkeys); dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp)); set_values(mp, NULL); - free_values(oldvalues, IS_DICT_SHARED(mp)); + if (oldvalues->embedded) { + assert(oldvalues->embedded == 1); + assert(oldvalues->valid == 1); + oldvalues->valid = 0; + } + else { + free_values(oldvalues, IS_DICT_SHARED(mp)); + } } else { // oldkeys is combined. if (oldkeys->dk_kind == DICT_KEYS_GENERAL) { @@ -2464,17 +2473,19 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, static void delete_index_from_values(PyDictValues *values, Py_ssize_t ix) { - uint8_t *size_ptr = ((uint8_t *)values)-2; - int size = *size_ptr; + uint8_t *array = get_insertion_order_array(values); + int size = values->size; + assert(size <= values->capacity); int i; - for (i = 1; size_ptr[-i] != ix; i++) { - assert(i <= size); + for (i = 0; array[i] != ix; i++) { + assert(i < size); } - assert(i <= size); + assert(i < size); + size--; for (; i < size; i++) { - size_ptr[-i] = size_ptr[-i-1]; + array[i] = array[i+1]; } - *size_ptr = size -1; + values->size = size; } static int @@ -2669,10 +2680,12 @@ clear_lock_held(PyObject *op) mp->ma_version_tag = new_version; /* ...then clear the keys and values */ if (oldvalues != NULL) { - n = oldkeys->dk_nentries; - for (i = 0; i < n; i++) - Py_CLEAR(oldvalues->values[i]); - free_values(oldvalues, IS_DICT_SHARED(mp)); + if (!oldvalues->embedded) { + n = oldkeys->dk_nentries; + for (i = 0; i < n; i++) + Py_CLEAR(oldvalues->values[i]); + free_values(oldvalues, IS_DICT_SHARED(mp)); + } dictkeys_decref(interp, oldkeys, false); } else { @@ -3059,10 +3072,12 @@ dict_dealloc(PyObject *self) PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { - for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { - Py_XDECREF(values->values[i]); + if (values->embedded == 0) { + for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { + Py_XDECREF(values->values[i]); + } + free_values(values, false); } - free_values(values, false); dictkeys_decref(interp, keys, false); } else if (keys != NULL) { @@ -3595,10 +3610,12 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe PyDictKeysObject *okeys = other->ma_keys; // If other is clean, combined, and just allocated, just clone it. - if (other->ma_values == NULL && - other->ma_used == okeys->dk_nentries && - (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || - USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)) { + if (mp->ma_values == NULL && + other->ma_values == NULL && + other->ma_used == okeys->dk_nentries && + (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || + USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used) + ) { uint64_t new_version = _PyDict_NotifyEvent( interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); PyDictKeysObject *keys = clone_combined_dict_keys(other); @@ -3608,11 +3625,6 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe ensure_shared_on_resize(mp); dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp)); mp->ma_keys = keys; - if (_PyDict_HasSplitTable(mp)) { - free_values(mp->ma_values, IS_DICT_SHARED(mp)); - mp->ma_values = NULL; - } - mp->ma_used = other->ma_used; mp->ma_version_tag = new_version; ASSERT_CONSISTENT(mp); @@ -3816,6 +3828,27 @@ dict_copy_impl(PyDictObject *self) return PyDict_Copy((PyObject *)self); } +/* Copies the values, but does not change the reference + * counts of the objects in the array. */ +static PyDictValues * +copy_values(PyDictValues *values) +{ + PyDictValues *newvalues = new_values(values->capacity); + if (newvalues == NULL) { + PyErr_NoMemory(); + return NULL; + } + newvalues->size = values->size; + uint8_t *values_order = get_insertion_order_array(values); + uint8_t *new_values_order = get_insertion_order_array(newvalues); + memcpy(new_values_order, values_order, values->capacity); + for (int i = 0; i < values->capacity; i++) { + newvalues->values[i] = values->values[i]; + } + assert(newvalues->embedded == 0); + return newvalues; +} + static PyObject * copy_lock_held(PyObject *o) { @@ -3833,26 +3866,23 @@ copy_lock_held(PyObject *o) if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; - size_t size = shared_keys_usable_size(mp->ma_keys); - PyDictValues *newvalues = new_values(size); - if (newvalues == NULL) + PyDictValues *newvalues = copy_values(mp->ma_values); + if (newvalues == NULL) { return PyErr_NoMemory(); + } split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues, false); return NULL; } - size_t prefix_size = ((uint8_t *)newvalues)[-1]; - memcpy(((char *)newvalues)-prefix_size, ((char *)mp->ma_values)-prefix_size, prefix_size-1); + for (size_t i = 0; i < newvalues->capacity; i++) { + Py_XINCREF(newvalues->values[i]); + } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(interp); dictkeys_incref(mp->ma_keys); - for (size_t i = 0; i < size; i++) { - PyObject *value = mp->ma_values->values[i]; - split_copy->ma_values->values[i] = Py_XNewRef(value); - } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; @@ -4406,8 +4436,10 @@ dict_traverse(PyObject *op, visitproc visit, void *arg) if (DK_IS_UNICODE(keys)) { if (_PyDict_HasSplitTable(mp)) { - for (i = 0; i < n; i++) { - Py_VISIT(mp->ma_values->values[i]); + if (!mp->ma_values->embedded) { + for (i = 0; i < n; i++) { + Py_VISIT(mp->ma_values->values[i]); + } } } else { @@ -5296,12 +5328,6 @@ acquire_key_value(PyObject **key_loc, PyObject *value, PyObject **value_loc, return 0; } -static Py_ssize_t -load_values_used_size(PyDictValues *values) -{ - return (Py_ssize_t)_Py_atomic_load_uint8(&DICT_VALUES_USED_SIZE(values)); -} - static int dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, PyObject **out_key, PyObject **out_value) @@ -5330,7 +5356,7 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, goto concurrent_modification; } - Py_ssize_t used = load_values_used_size(values); + Py_ssize_t used = (Py_ssize_t)_Py_atomic_load_uint8(&values->size); if (i >= used) { goto fail; } @@ -6539,15 +6565,15 @@ _PyDict_NewKeysForClass(void) return keys; } -#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) - -int +void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) { assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictKeysObject *keys = CACHED_KEYS(tp); assert(keys != NULL); + OBJECT_STAT_INC(inline_values); #ifdef Py_GIL_DISABLED Py_ssize_t usable = _Py_atomic_load_ssize_relaxed(&keys->dk_usable); if (usable > 1) { @@ -6563,49 +6589,19 @@ _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) } #endif size_t size = shared_keys_usable_size(keys); - PyDictValues *values = new_values(size); - if (values == NULL) { - PyErr_NoMemory(); - return -1; - } - assert(((uint8_t *)values)[-1] >= (size + 2)); - ((uint8_t *)values)[-2] = 0; + PyDictValues *values = _PyObject_InlineValues(obj); + assert(size < 256); + values->capacity = (uint8_t)size; + values->size = 0; + values->embedded = 1; + values->valid = 1; for (size_t i = 0; i < size; i++) { values->values[i] = NULL; } - _PyDictOrValues_SetValues(_PyObject_DictOrValuesPointer(obj), values); - return 0; -} - -int -_PyObject_InitializeDict(PyObject *obj) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyTypeObject *tp = Py_TYPE(obj); - if (tp->tp_dictoffset == 0) { - return 0; - } - if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - OBJECT_STAT_INC(new_values); - return _PyObject_InitInlineValues(obj, tp); - } - PyObject *dict; - if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { - dictkeys_incref(CACHED_KEYS(tp)); - dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); - } - else { - dict = PyDict_New(); - } - if (dict == NULL) { - return -1; - } - PyObject **dictptr = _PyObject_ComputedDictPointer(obj); - *dictptr = dict; - return 0; + _PyObject_ManagedDictPointer(obj)->dict = NULL; } -static PyObject * +static PyDictObject * make_dict_from_instance_attributes(PyInterpreterState *interp, PyDictKeysObject *keys, PyDictValues *values) { @@ -6620,56 +6616,24 @@ make_dict_from_instance_attributes(PyInterpreterState *interp, track += _PyObject_GC_MAY_BE_TRACKED(val); } } - PyObject *res = new_dict(interp, keys, values, used, 0); + PyDictObject *res = (PyDictObject *)new_dict(interp, keys, values, used, 0); if (track && res) { _PyObject_GC_TRACK(res); } return res; } -PyObject * -_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values) +PyDictObject * +_PyObject_MakeDictFromInstanceAttributes(PyObject *obj) { + PyDictValues *values = _PyObject_InlineValues(obj); PyInterpreterState *interp = _PyInterpreterState_GET(); PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); OBJECT_STAT_INC(dict_materialized_on_request); return make_dict_from_instance_attributes(interp, keys, values); } -// Return true if the dict was dematerialized, false otherwise. -bool -_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 false; - } - // It's likely that this dict still shares its keys (if it was materialized - // on request and not heavily modified): - if (!PyDict_CheckExact(dict)) { - return false; - } - assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE)); - if (dict->ma_keys != CACHED_KEYS(Py_TYPE(obj)) || - !has_unique_reference((PyObject *)dict)) - { - return false; - } - ensure_shared_on_resize(dict); - 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 true; -} int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, @@ -6679,7 +6643,7 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); assert(keys != NULL); assert(values != NULL); - assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); Py_ssize_t ix = DKIX_EMPTY; if (PyUnicode_CheckExact(name)) { Py_hash_t hash = unicode_get_hash(name); @@ -6717,18 +6681,21 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, } #endif } + PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; if (ix == DKIX_EMPTY) { - PyObject *dict = make_dict_from_instance_attributes( - interp, keys, values); if (dict == NULL) { - return -1; + dict = make_dict_from_instance_attributes( + interp, keys, values); + if (dict == NULL) { + return -1; + } + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; } - _PyObject_DictOrValuesPointer(obj)->dict = dict; if (value == NULL) { - return PyDict_DelItem(dict, name); + return PyDict_DelItem((PyObject *)dict, name); } else { - return PyDict_SetItem(dict, name, value); + return PyDict_SetItem((PyObject *)dict, name, value); } } PyObject *old_value = values->values[ix]; @@ -6741,10 +6708,18 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, return -1; } _PyDictValues_AddToInsertionOrder(values, ix); + if (dict) { + assert(dict->ma_values == values); + dict->ma_used++; + } } else { if (value == NULL) { delete_index_from_values(values, ix); + if (dict) { + assert(dict->ma_values == values); + dict->ma_used--; + } } Py_DECREF(old_value); } @@ -6760,9 +6735,9 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj) { PyTypeObject *tp = Py_TYPE(obj); CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + if (_PyManagedDictPointer_IsValues(*managed_dict)) { + PyDictValues *values = _PyManagedDictPointer_GetValues(*managed_dict); int size = ((uint8_t *)values)[-2]; int count = 0; PyDictKeysObject *keys = CACHED_KEYS(tp); @@ -6774,8 +6749,8 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj) CHECK(size == count); } else { - if (dorv_ptr->dict != NULL) { - CHECK(PyDict_Check(dorv_ptr->dict)); + if (managed_dict->dict != NULL) { + CHECK(PyDict_Check(managed_dict->dict)); } } return 1; @@ -6804,23 +6779,27 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) if (tp->tp_dictoffset == 0) { return 1; } - PyObject *dict; - if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(dorv)) { + PyDictObject *dict; + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { PyDictKeysObject *keys = CACHED_KEYS(tp); for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - if (_PyDictOrValues_GetValues(dorv)->values[i] != NULL) { + if (values->values[i] != NULL) { return 0; } } return 1; } - dict = _PyDictOrValues_GetDict(dorv); + dict = _PyObject_ManagedDictPointer(obj)->dict; + } + else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = managed_dict->dict; } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); - dict = *dictptr; + dict = (PyDictObject *)*dictptr; } if (dict == NULL) { return 1; @@ -6828,23 +6807,6 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) return ((PyDictObject *)dict)->ma_used == 0; } -void -_PyObject_FreeInstanceAttributes(PyObject *self) -{ - PyTypeObject *tp = Py_TYPE(self); - assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self); - if (!_PyDictOrValues_IsValues(dorv)) { - return; - } - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_XDECREF(values->values[i]); - } - free_values(values, IS_DICT_SHARED((PyDictObject*)self)); -} - int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) { @@ -6852,74 +6814,101 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return 0; } - assert(tp->tp_dictoffset); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(dorv)) { - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_VISIT(values->values[i]); + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { + for (Py_ssize_t i = 0; i < values->capacity; i++) { + Py_VISIT(values->values[i]); + } + return 0; } } - else { - PyObject *dict = _PyDictOrValues_GetDict(dorv); - Py_VISIT(dict); - } + Py_VISIT(_PyObject_ManagedDictPointer(obj)->dict); return 0; } void PyObject_ClearManagedDict(PyObject *obj) { + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); PyTypeObject *tp = Py_TYPE(obj); - if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { - return; - } - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - PyDictKeysObject *keys = CACHED_KEYS(tp); - for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { - Py_CLEAR(values->values[i]); - } - dorv_ptr->dict = NULL; - free_values(values, IS_DICT_SHARED((PyDictObject*)obj)); - } - else { - PyObject *dict = dorv_ptr->dict; + if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; if (dict) { - dorv_ptr->dict = NULL; + _PyDict_DetachFromObject(dict, obj); + _PyObject_ManagedDictPointer(obj)->dict = NULL; Py_DECREF(dict); } + else { + PyDictValues *values = _PyObject_InlineValues(obj); + if (values->valid) { + for (Py_ssize_t i = 0; i < values->capacity; i++) { + Py_CLEAR(values->values[i]); + } + values->valid = 0; + } + } + } + else { + Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict); + } + assert(_PyObject_InlineValuesConsistencyCheck(obj)); +} + +int +_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) +{ + assert(_PyObject_ManagedDictPointer(obj)->dict == mp); + assert(_PyObject_InlineValuesConsistencyCheck(obj)); + if (mp->ma_values == NULL || mp->ma_values != _PyObject_InlineValues(obj)) { + return 0; + } + assert(mp->ma_values->embedded == 1); + assert(mp->ma_values->valid == 1); + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + Py_BEGIN_CRITICAL_SECTION(mp); + mp->ma_values = copy_values(mp->ma_values); + _PyObject_InlineValues(obj)->valid = 0; + Py_END_CRITICAL_SECTION(); + if (mp->ma_values == NULL) { + return -1; } + assert(_PyObject_InlineValuesConsistencyCheck(obj)); + ASSERT_CONSISTENT(mp); + return 0; } PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { - PyObject *dict; PyInterpreterState *interp = _PyInterpreterState_GET(); PyTypeObject *tp = Py_TYPE(obj); if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + PyDictObject *dict = managed_dict->dict; + if (dict == NULL && + (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_InlineValues(obj)->valid + ) { + PyDictValues *values = _PyObject_InlineValues(obj); OBJECT_STAT_INC(dict_materialized_on_request); dict = make_dict_from_instance_attributes( interp, CACHED_KEYS(tp), values); if (dict != NULL) { - dorv_ptr->dict = dict; + managed_dict->dict = (PyDictObject *)dict; } } else { - dict = _PyDictOrValues_GetDict(*dorv_ptr); + dict = managed_dict->dict; 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 = (PyDictObject *)new_dict_with_shared_keys(interp, CACHED_KEYS(tp)); + managed_dict->dict = (PyDictObject *)dict; } } + return Py_XNewRef((PyObject *)dict); } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -6928,7 +6917,7 @@ PyObject_GenericGetDict(PyObject *obj, void *context) "This object has no __dict__"); return NULL; } - dict = *dictptr; + PyObject *dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { @@ -6940,8 +6929,8 @@ PyObject_GenericGetDict(PyObject *obj, void *context) *dictptr = dict = PyDict_New(); } } + return Py_XNewRef(dict); } - return Py_XNewRef(dict); } int @@ -6958,7 +6947,7 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { - assert(!_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)); + assert(!_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)); dictkeys_incref(cached); dict = new_dict_with_shared_keys(interp, cached); if (dict == NULL) @@ -7118,3 +7107,24 @@ _PyDict_SendEvent(int watcher_bits, watcher_bits >>= 1; } } + +#ifndef NDEBUG +static int +_PyObject_InlineValuesConsistencyCheck(PyObject *obj) +{ + if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) { + return 1; + } + assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = (PyDictObject *)_PyObject_ManagedDictPointer(obj)->dict; + if (dict == NULL) { + return 1; + } + if (dict->ma_values == _PyObject_InlineValues(obj) || + _PyObject_InlineValues(obj)->valid == 0) { + return 1; + } + assert(0); + return 0; +} +#endif diff --git a/Objects/object.c b/Objects/object.c index b4f0fd4d7db9..60642d899bca 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1396,16 +1396,16 @@ _PyObject_GetDictPtr(PyObject *obj) if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return _PyObject_ComputedDictPointer(obj); } - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, _PyDictOrValues_GetValues(*dorv_ptr)); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + if (managed_dict->dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(obj); if (dict == NULL) { PyErr_Clear(); return NULL; } - dorv_ptr->dict = dict; + managed_dict->dict = dict; } - return &dorv_ptr->dict; + return (PyObject **)&managed_dict->dict; } PyObject * @@ -1474,21 +1474,19 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } } PyObject *dict; - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); - if (attr != NULL) { - *method = attr; - Py_XDECREF(descr); - return 0; - } - dict = NULL; - } - else { - dict = dorv_ptr->dict; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + PyDictValues *values = _PyObject_InlineValues(obj); + PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); + if (attr != NULL) { + *method = attr; + Py_XDECREF(descr); + return 0; } + dict = NULL; + } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = (PyObject *)managed_dict->dict; } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); @@ -1581,29 +1579,27 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } } if (dict == NULL) { - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); - if (PyUnicode_CheckExact(name)) { - res = _PyObject_GetInstanceAttribute(obj, values, name); - if (res != NULL) { - goto done; - } - } - else { - dict = _PyObject_MakeDictFromInstanceAttributes(obj, values); - if (dict == NULL) { - res = NULL; - goto done; - } - dorv_ptr->dict = dict; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + PyDictValues *values = _PyObject_InlineValues(obj); + if (PyUnicode_CheckExact(name)) { + res = _PyObject_GetInstanceAttribute(obj, values, name); + if (res != NULL) { + goto done; } } else { - dict = _PyDictOrValues_GetDict(*dorv_ptr); + dict = (PyObject *)_PyObject_MakeDictFromInstanceAttributes(obj); + if (dict == NULL) { + res = NULL; + goto done; + } + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)dict; } } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer* managed_dict = _PyObject_ManagedDictPointer(obj); + dict = (PyObject *)managed_dict->dict; + } else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); if (dictptr) { @@ -1697,22 +1693,14 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, if (dict == NULL) { PyObject **dictptr; - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - res = _PyObject_StoreInstanceAttribute( - obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value); - goto error_check; - } - dictptr = &dorv_ptr->dict; - if (*dictptr == NULL) { - if (_PyObject_InitInlineValues(obj, tp) < 0) { - goto done; - } - res = _PyObject_StoreInstanceAttribute( - obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value); - goto error_check; - } + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_InlineValues(obj)->valid) { + res = _PyObject_StoreInstanceAttribute( + obj, _PyObject_InlineValues(obj), name, value); + goto error_check; + } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj); + dictptr = (PyObject **)&managed_dict->dict; } else { dictptr = _PyObject_ComputedDictPointer(obj); @@ -1783,9 +1771,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) { PyObject **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { - if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT) && - _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(obj))) - { + if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) && + _PyObject_ManagedDictPointer(obj)->dict == NULL + ) { /* Was unable to convert to dict */ PyErr_NoMemory(); } diff --git a/Objects/object_layout.md b/Objects/object_layout.md index 4f379bed8d77..352409425ee8 100644 --- a/Objects/object_layout.md +++ b/Objects/object_layout.md @@ -16,7 +16,45 @@ Since the introduction of the cycle GC, there has also been a pre-header. Before 3.11, this pre-header was two words in size. It should be considered opaque to all code except the cycle GC. -## 3.11 pre-header +### 3.13 + +In 3.13, the values array is embedded into the object, so there is no +need for a values pointer (it is just a fixed offset into the object). +So the pre-header is these two fields: + +* weakreflist +* dict_pointer + +If the object has no physical dictionary, then the ``dict_pointer`` +is set to `NULL`. + + +
+ 3.12 + +### 3.12 + +In 3.12, the pointer to the list of weak references is added to the +pre-header. In order to make space for it, the ``dict`` and ``values`` +pointers are combined into a single tagged pointer: + +* weakreflist +* dict_or_values + +If the object has no physical dictionary, then the ``dict_or_values`` +has its low bit set to one, and points to the values array. +If the object has a physical dictionary, then the ``dict_or_values`` +has its low bit set to zero, and points to the dictionary. + +The untagged form is chosen for the dictionary pointer, rather than +the values pointer, to enable the (legacy) C-API function +`_PyObject_GetDictPtr(PyObject *obj)` to work. +
+ +
+ 3.11 + +### 3.11 In 3.11 the pre-header was extended to include pointers to the VM managed ``__dict__``. The reason for moving the ``__dict__`` to the pre-header is that it allows @@ -33,27 +71,49 @@ The values pointer refers to the ``PyDictValues`` array which holds the values of the objects's attributes. Should the dictionary be needed, then ``values`` is set to ``NULL`` and the ``dict`` field points to the dictionary. +
-## 3.12 pre-header +## Layout of a "normal" Python object -In 3.12, the pointer to the list of weak references is added to the -pre-header. In order to make space for it, the ``dict`` and ``values`` -pointers are combined into a single tagged pointer: +A "normal" Python object is one that doesn't inherit from a builtin +class, doesn't have slots. + +### 3.13 + +In 3.13 the values are embedded into the object, as follows: * weakreflist * dict_or_values +* GC 1 +* GC 2 +* ob_refcnt +* ob_type +* Inlined values: + * Flags + * values 0 + * values 1 + * ... + * Insertion order bytes -If the object has no physical dictionary, then the ``dict_or_values`` -has its low bit set to one, and points to the values array. -If the object has a physical dictionary, then the ``dict_or_values`` -has its low bit set to zero, and points to the dictionary. +This has all the advantages of the layout used in 3.12, plus: +* Access to values is even faster as there is one less load +* Fast access is mostly maintained when the `__dict__` is materialized -The untagged form is chosen for the dictionary pointer, rather than -the values pointer, to enable the (legacy) C-API function -`_PyObject_GetDictPtr(PyObject *obj)` to work. +![Layout of "normal" object in 3.13](./object_layout_313.png) + +For objects with opaque parts defined by a C extension, +the layout is much the same as for 3.12 +![Layout of "full" object in 3.13](./object_layout_full_313.png) -## Layout of a "normal" Python object in 3.12: + +
+ 3.12 + +### 3.12: + +In 3.12, the header and pre-header form the entire object for "normal" +Python objects: * weakreflist * dict_or_values @@ -62,9 +122,6 @@ the values pointer, to enable the (legacy) C-API function * ob_refcnt * ob_type -For a "normal" Python object, one that doesn't inherit from a builtin -class or have slots, the header and pre-header form the entire object. - ![Layout of "normal" object in 3.12](./object_layout_312.png) There are several advantages to this layout: @@ -79,4 +136,6 @@ The full layout object, with an opaque part defined by a C extension, and `__slots__` looks like this: ![Layout of "full" object in 3.12](./object_layout_full_312.png) +
+ diff --git a/Objects/object_layout_312.gv b/Objects/object_layout_312.gv index c0068d785685..731a25332b3a 100644 --- a/Objects/object_layout_312.gv +++ b/Objects/object_layout_312.gv @@ -20,6 +20,7 @@ digraph ideal { shape = none label = < + diff --git a/Objects/object_layout_312.png b/Objects/object_layout_312.png index 396dab183b3e9b2f39edb49b033a612e66d3a09a..a63d095ea0b19ebebb86ccadb964278bcfe0648b 100644 GIT binary patch literal 33040 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

qI;(m!P0d>z%}(g%y*J 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 LRAs+No4)!VYXwxy literal 30688 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 GeEbh{N2Wjk diff --git a/Objects/object_layout_313.gv b/Objects/object_layout_313.gv new file mode 100644 index 000000000000..d85352876f27 --- /dev/null +++ b/Objects/object_layout_313.gv @@ -0,0 +1,45 @@ +digraph ideal { + + rankdir = "LR" + + + object [ + shape = none + label = <

values
Insertion order
values[0]
values[1]
...
+ + + + + + + + + + + + +
object
weakrefs
dict pointer
GC info 0
GC info 1
refcount
__class__
values flags
values[0]
values[1]
...
Insertion order
> + + ] + + class [ + shape = none + label = < + + + + + +
class
...
dict_offset
...
cached_keys
> + ] + + keys [label = "dictionary keys"; fillcolor="lightgreen"; style="filled"] + NULL [ label = " NULL"; shape="plain"] + object:w -> NULL + object:h -> class:head + object:dv -> NULL + class:k -> keys + + oop [ label = "pointer"; shape="plain"] + oop -> object:r +} diff --git a/Objects/object_layout_313.png b/Objects/object_layout_313.png new file mode 100644 index 0000000000000000000000000000000000000000..f7059c286c84e6cd9a71f59b640a628a110d80ac GIT binary patch literal 35055 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 + object + weakrefs + dict pointer + GC info 0 + GC info 1 + refcount + __class__ + opaque (extension) data + ... + __slot__ 0 + ... + > + ] + + oop [ label = "pointer"; shape="plain"] + oop -> object:r +} diff --git a/Objects/object_layout_full_313.png b/Objects/object_layout_full_313.png new file mode 100644 index 0000000000000000000000000000000000000000..7352ec69e5d8db2c59135cf39aa0e6f7dd323cc9 GIT binary patch literal 16983 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 & Py_TPFLAGS_MANAGED_DICT); + assert(type->tp_flags & Py_TPFLAGS_INLINE_VALUES); assert(_PyType_IS_GC(type)); assert(type->tp_new == PyBaseObject_Type.tp_new); assert(type->tp_alloc == PyType_GenericAlloc); @@ -1870,11 +1870,6 @@ _PyType_NewManagedObject(PyTypeObject *type) if (obj == NULL) { return PyErr_NoMemory(); } - _PyObject_DictOrValuesPointer(obj)->dict = NULL; - if (_PyObject_InitInlineValues(obj, type)) { - Py_DECREF(obj); - return NULL; - } return obj; } @@ -1888,9 +1883,13 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) * flag to indicate when that is safe) it does not seem worth the memory * savings. An example type that doesn't need the +1 is a subclass of * tuple. See GH-100659 and GH-81381. */ - const size_t size = _PyObject_VAR_SIZE(type, nitems+1); + size_t size = _PyObject_VAR_SIZE(type, nitems+1); const size_t presize = _PyType_PreHeaderSize(type); + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + assert(type->tp_itemsize == 0); + size += _PyInlineValuesSize(type); + } char *alloc = _PyObject_MallocWithType(type, size + presize); if (alloc == NULL) { return PyErr_NoMemory(); @@ -1911,6 +1910,9 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) else { _PyObject_InitVar((PyVarObject *)obj, type, nitems); } + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + _PyObject_InitInlineValues(obj, type); + } return obj; } @@ -2060,6 +2062,10 @@ subtype_clear(PyObject *self) if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { PyObject_ClearManagedDict(self); } + else { + assert((base->tp_flags & Py_TPFLAGS_INLINE_VALUES) == + (type->tp_flags & Py_TPFLAGS_INLINE_VALUES)); + } } else if (type->tp_dictoffset != base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self); @@ -2210,14 +2216,7 @@ subtype_dealloc(PyObject *self) /* If we added a dict, DECREF it, or free inline values. */ if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(self); - if (_PyDictOrValues_IsValues(*dorv_ptr)) { - _PyObject_FreeInstanceAttributes(self); - } - else { - Py_XDECREF(_PyDictOrValues_GetDict(*dorv_ptr)); - } - dorv_ptr->values = NULL; + PyObject_ClearManagedDict(self); } else if (type->tp_dictoffset && !base->tp_dictoffset) { PyObject **dictptr = _PyObject_ComputedDictPointer(self); @@ -3161,19 +3160,26 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context) return func(descr, obj, value); } /* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */ - dictptr = _PyObject_GetDictPtr(obj); - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return -1; - } if (value != NULL && !PyDict_Check(value)) { PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, " "not a '%.200s'", Py_TYPE(value)->tp_name); return -1; } - Py_XSETREF(*dictptr, Py_XNewRef(value)); + if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + PyObject_ClearManagedDict(obj); + _PyObject_ManagedDictPointer(obj)->dict = (PyDictObject *)Py_XNewRef(value); + } + else { + dictptr = _PyObject_ComputedDictPointer(obj); + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return -1; + } + Py_CLEAR(*dictptr); + *dictptr = Py_XNewRef(value); + } return 0; } @@ -5849,10 +5855,6 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (obj == NULL) { return NULL; } - if (_PyObject_InitializeDict(obj)) { - Py_DECREF(obj); - return NULL; - } return obj; } @@ -6036,6 +6038,11 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* !same_slots_added(newbase, oldbase))) { goto differs; } + if ((oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) != + ((newto->tp_flags & Py_TPFLAGS_INLINE_VALUES))) + { + goto differs; + } /* The above does not check for the preheader */ if ((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == ((newto->tp_flags & Py_TPFLAGS_PREHEADER))) @@ -6137,14 +6144,18 @@ object_set_class(PyObject *self, PyObject *value, void *closure) if (compatible_for_assignment(oldto, newto, "__class__")) { /* Changing the class will change the implicit dict keys, * so we must materialize the dictionary first. */ - assert((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == (newto->tp_flags & Py_TPFLAGS_PREHEADER)); - _PyObject_GetDictPtr(self); - if (oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT && - _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(self))) - { - /* Was unable to convert to dict */ - PyErr_NoMemory(); - return -1; + if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = _PyObject_ManagedDictPointer(self)->dict; + if (dict == NULL) { + dict = (PyDictObject *)_PyObject_MakeDictFromInstanceAttributes(self); + if (dict == NULL) { + return -1; + } + _PyObject_ManagedDictPointer(self)->dict = dict; + } + if (_PyDict_DetachFromObject(dict, self)) { + return -1; + } } if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) { Py_INCREF(newto); @@ -7774,6 +7785,9 @@ type_ready_managed_dict(PyTypeObject *type) return -1; } } + if (type->tp_itemsize == 0 && type->tp_basicsize == sizeof(PyObject)) { + type->tp_flags |= Py_TPFLAGS_INLINE_VALUES; + } return 0; } @@ -7901,6 +7915,8 @@ PyType_Ready(PyTypeObject *type) /* Historically, all static types were immutable. See bpo-43908 */ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; + /* Static types must be immortal */ + _Py_SetImmortalUntracked((PyObject *)type); } int res; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index bfb378c4a415..ce208aac9c79 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1897,14 +1897,12 @@ dummy_func( 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) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid); } split op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; DEOPT_IF(attr == NULL); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1947,16 +1945,15 @@ dummy_func( op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv)); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); } op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -2070,16 +2067,17 @@ dummy_func( DISPATCH_INLINED(new_frame); } - op(_GUARD_DORV_VALUES, (owner -- owner)) { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + op(_GUARD_DORV_NO_DICT, (owner -- owner)) { + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict); + DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0); } op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) { - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -2094,7 +2092,7 @@ dummy_func( macro(STORE_ATTR_INSTANCE_VALUE) = unused/1 + _GUARD_TYPE_VERSION + - _GUARD_DORV_VALUES + + _GUARD_DORV_NO_DICT + _STORE_ATTR_INSTANCE_VALUE; inst(STORE_ATTR_WITH_HINT, (unused/1, type_version/2, hint/1, value, owner --)) { @@ -2102,9 +2100,8 @@ dummy_func( assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv)); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -2898,9 +2895,8 @@ dummy_func( } op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner)) { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid); } op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { @@ -2972,10 +2968,9 @@ dummy_func( unused/2 + _LOAD_ATTR_NONDESCRIPTOR_NO_DICT; - op(_CHECK_ATTR_METHOD_LAZY_DICT, (owner -- owner)) { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + op(_CHECK_ATTR_METHOD_LAZY_DICT, (dictoffset/1, owner -- owner)) { + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL); } @@ -2993,7 +2988,7 @@ dummy_func( unused/1 + _GUARD_TYPE_VERSION + _CHECK_ATTR_METHOD_LAZY_DICT + - unused/2 + + unused/1 + _LOAD_ATTR_METHOD_LAZY_DICT; inst(INSTRUMENTED_CALL, (unused/3 -- )) { @@ -3294,6 +3289,7 @@ dummy_func( DEOPT_IF(!PyType_Check(callable)); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version)); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; PyCodeObject *code = (PyCodeObject *)init->func_code; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ce0dc235c54f..82f2171f1ede 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1753,9 +1753,8 @@ PyObject *owner; 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); - if (!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!_PyObject_InlineValues(owner)->valid) JUMP_TO_JUMP_TARGET(); break; } @@ -1766,8 +1765,7 @@ (void)null; owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; if (attr == NULL) JUMP_TO_JUMP_TARGET(); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1784,8 +1782,7 @@ (void)null; owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; if (attr == NULL) JUMP_TO_JUMP_TARGET(); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -1837,9 +1834,8 @@ PyObject *owner; owner = stack_pointer[-1]; assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - if (_PyDictOrValues_IsValues(dorv)) JUMP_TO_JUMP_TARGET(); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (dict == NULL) JUMP_TO_JUMP_TARGET(); assert(PyDict_CheckExact((PyObject *)dict)); break; @@ -1852,8 +1848,8 @@ oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; uint16_t hint = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (hint >= (size_t)dict->ma_keys->dk_nentries) JUMP_TO_JUMP_TARGET(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -1967,12 +1963,13 @@ /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _GUARD_DORV_VALUES: { + case _GUARD_DORV_NO_DICT: { PyObject *owner; owner = stack_pointer[-1]; - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - if (!_PyDictOrValues_IsValues(dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_ManagedDictPointer(owner)->dict) JUMP_TO_JUMP_TARGET(); + if (_PyObject_InlineValues(owner)->valid == 0) JUMP_TO_JUMP_TARGET(); break; } @@ -1982,9 +1979,9 @@ owner = stack_pointer[-1]; value = stack_pointer[-2]; uint16_t index = (uint16_t)CURRENT_OPERAND(); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -2568,9 +2565,8 @@ case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: { PyObject *owner; owner = stack_pointer[-1]; - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - if (!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) JUMP_TO_JUMP_TARGET(); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!_PyObject_InlineValues(owner)->valid) JUMP_TO_JUMP_TARGET(); break; } @@ -2658,9 +2654,9 @@ case _CHECK_ATTR_METHOD_LAZY_DICT: { PyObject *owner; owner = stack_pointer[-1]; - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND(); + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ if (dict != NULL) JUMP_TO_JUMP_TARGET(); break; diff --git a/Python/gc.c b/Python/gc.c index a37c1b144e57..a48738835ffa 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -2031,11 +2031,16 @@ gc_alloc(PyTypeObject *tp, size_t basicsize, size_t presize) return op; } + PyObject * _PyObject_GC_New(PyTypeObject *tp) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp), presize); + size_t size = _PyObject_SIZE(tp); + if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) { + size += _PyInlineValuesSize(tp); + } + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 4524382e4f68..7e4137a8e342 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1639,7 +1639,11 @@ PyObject * _PyObject_GC_New(PyTypeObject *tp) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp), presize); + size_t size = _PyObject_SIZE(tp); + if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) { + size += _PyInlineValuesSize(tp); + } + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e8e2397b11cd..6ee794a05b51 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -870,6 +870,7 @@ DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); + assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; PyCodeObject *code = (PyCodeObject *)init->func_code; @@ -3680,15 +3681,13 @@ // _CHECK_MANAGED_OBJECT_HAS_VALUES { 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) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _LOAD_ATTR_INSTANCE_VALUE { uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; + attr = _PyObject_InlineValues(owner)->values[index]; DEOPT_IF(attr == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(attr); @@ -3721,13 +3720,13 @@ } // _CHECK_ATTR_METHOD_LAZY_DICT { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + uint16_t dictoffset = read_u16(&this_instr[4].cache); + char *ptr = ((char *)owner) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = *(PyObject **)ptr; /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); } - /* Skip 2 cache entries */ + /* Skip 1 cache entry */ // _LOAD_ATTR_METHOD_LAZY_DICT { PyObject *descr = read_obj(&this_instr[6].cache); @@ -3798,9 +3797,8 @@ } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _GUARD_KEYS_VERSION { @@ -3914,9 +3912,8 @@ } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(!_PyObject_InlineValues(owner)->valid, LOAD_ATTR); } // _GUARD_KEYS_VERSION { @@ -4026,17 +4023,16 @@ // _CHECK_ATTR_WITH_HINT { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); } // _LOAD_ATTR_WITH_HINT { uint16_t hint = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -5315,19 +5311,20 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - // _GUARD_DORV_VALUES + // _GUARD_DORV_NO_DICT { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + DEOPT_IF(_PyObject_ManagedDictPointer(owner)->dict, STORE_ATTR); + DEOPT_IF(_PyObject_InlineValues(owner)->valid == 0, STORE_ATTR); } // _STORE_ATTR_INSTANCE_VALUE value = stack_pointer[-2]; { uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); + assert(_PyObject_ManagedDictPointer(owner)->dict == NULL); + PyDictValues *values = _PyObject_InlineValues(owner); PyObject *old_value = values->values[index]; values->values[index] = value; if (old_value == NULL) { @@ -5389,9 +5386,8 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; DEOPT_IF(dict == NULL, STORE_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index df73cc091dea..b4a1da8aec14 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1104,7 +1104,7 @@ /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */ - case _GUARD_DORV_VALUES: { + case _GUARD_DORV_NO_DICT: { break; } diff --git a/Python/specialize.c b/Python/specialize.c index c1edf8842faf..f1e32d05af77 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -188,7 +188,7 @@ print_object_stats(FILE *out, ObjectStats *stats) fprintf(out, "Object allocations to 4 kbytes: %" PRIu64 "\n", stats->allocations4k); fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big); fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees); - fprintf(out, "Object new values: %" PRIu64 "\n", stats->new_values); + fprintf(out, "Object inline values: %" PRIu64 "\n", stats->inline_values); fprintf(out, "Object interpreter increfs: %" PRIu64 "\n", stats->interpreter_increfs); fprintf(out, "Object interpreter decrefs: %" PRIu64 "\n", stats->interpreter_decrefs); fprintf(out, "Object increfs: %" PRIu64 "\n", stats->increfs); @@ -197,7 +197,6 @@ print_object_stats(FILE *out, ObjectStats *stats) 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); @@ -479,12 +478,11 @@ _PyCode_Quicken(PyCodeObject *code) #define SPEC_FAIL_ATTR_NOT_MANAGED_DICT 18 #define SPEC_FAIL_ATTR_NON_STRING_OR_SPLIT 19 #define SPEC_FAIL_ATTR_MODULE_ATTR_NOT_FOUND 20 - #define SPEC_FAIL_ATTR_SHADOWED 21 #define SPEC_FAIL_ATTR_BUILTIN_CLASS_METHOD 22 #define SPEC_FAIL_ATTR_CLASS_METHOD_OBJ 23 #define SPEC_FAIL_ATTR_OBJECT_SLOT 24 -#define SPEC_FAIL_ATTR_HAS_MANAGED_DICT 25 + #define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26 #define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27 #define SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION 28 @@ -558,6 +556,7 @@ _PyCode_Quicken(PyCodeObject *code) #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29 #define SPEC_FAIL_CALL_INIT_NOT_SIMPLE 30 #define SPEC_FAIL_CALL_METACLASS 31 +#define SPEC_FAIL_CALL_INIT_NOT_INLINE_VALUES 32 /* COMPARE_OP */ #define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12 @@ -829,11 +828,7 @@ specialize_dict_access( return 0; } _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - if (_PyDictOrValues_IsValues(*dorv) || - _PyObject_MakeInstanceAttributesFromDict(owner, dorv)) - { - // Virtual dictionary + if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES && _PyObject_InlineValues(owner)->valid) { PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; assert(PyUnicode_CheckExact(name)); Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); @@ -850,7 +845,8 @@ specialize_dict_access( instr->op.code = values_op; } else { - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(*dorv); + PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(owner); + PyDictObject *dict = managed_dict->dict; if (dict == NULL || !PyDict_CheckExact(dict)) { SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT); return 0; @@ -1258,15 +1254,8 @@ PyObject *descr, DescriptorClassification kind, bool is_method) 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); + if (owner_cls->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; - if (!_PyDictOrValues_IsValues(*dorv) && - !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)) - { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); - return 0; - } Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SHADOWED); @@ -1282,10 +1271,16 @@ PyObject *descr, DescriptorClassification kind, bool is_method) instr->op.code = is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; } else { - Py_ssize_t dictoffset = owner_cls->tp_dictoffset; - if (dictoffset < 0 || dictoffset > INT16_MAX) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); - return 0; + Py_ssize_t dictoffset; + if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + dictoffset = MANAGED_DICT_OFFSET; + } + else { + dictoffset = owner_cls->tp_dictoffset; + if (dictoffset < 0 || dictoffset > INT16_MAX + MANAGED_DICT_OFFSET) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } } if (dictoffset == 0) { instr->op.code = is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT; @@ -1296,8 +1291,12 @@ PyObject *descr, DescriptorClassification kind, bool is_method) SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; } - assert(owner_cls->tp_dictoffset > 0); - assert(owner_cls->tp_dictoffset <= INT16_MAX); + /* Cache entries must be unsigned values, so we offset the + * dictoffset by MANAGED_DICT_OFFSET. + * We do the reverese offset in LOAD_ATTR_METHOD_LAZY_DICT */ + dictoffset -= MANAGED_DICT_OFFSET; + assert(((uint16_t)dictoffset) == dictoffset); + cache->dict_offset = (uint16_t)dictoffset; instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT; } else { @@ -1729,8 +1728,8 @@ get_init_for_simple_managed_python_class(PyTypeObject *tp) SPECIALIZATION_FAIL(CALL, SPEC_FAIL_OVERRIDDEN); return NULL; } - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_NO_DICT); + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_INIT_NOT_INLINE_VALUES); return NULL; } if (!(tp->tp_flags & Py_TPFLAGS_HEAPTYPE)) { diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index ddafcf99ca1e..4261378d4591 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -359,11 +359,10 @@ def has_error_without_pop(op: parser.InstDef) -> bool: NON_ESCAPING_FUNCTIONS = ( "Py_INCREF", - "_PyDictOrValues_IsValues", - "_PyObject_DictOrValuesPointer", - "_PyDictOrValues_GetValues", + "_PyManagedDictPointer_IsValues", + "_PyObject_ManagedDictPointer", + "_PyObject_InlineValues", "_PyDictValues_AddToInsertionOrder", - "_PyObject_MakeInstanceAttributesFromDict", "Py_DECREF", "_Py_DECREF_SPECIALIZED", "DECREF_INPUTS_AND_REUSE_FLOAT", diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 656667ac9397..74165acd8311 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -66,10 +66,12 @@ def _type_unsigned_short_ptr(): def _type_unsigned_int_ptr(): return gdb.lookup_type('unsigned int').pointer() - def _sizeof_void_p(): return gdb.lookup_type('void').pointer().sizeof +def _sizeof_pyobject(): + return gdb.lookup_type('PyObject').sizeof + def _managed_dict_offset(): # See pycore_object.h pyobj = gdb.lookup_type("PyObject") @@ -79,6 +81,7 @@ def _managed_dict_offset(): return -3 * _sizeof_void_p() +Py_TPFLAGS_INLINE_VALUES = (1 << 2) Py_TPFLAGS_MANAGED_DICT = (1 << 4) Py_TPFLAGS_HEAPTYPE = (1 << 9) Py_TPFLAGS_LONG_SUBCLASS = (1 << 24) @@ -493,11 +496,12 @@ class HeapTypeObjectPtr(PyObjectPtr): has_values = int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT if not has_values: return None - ptr = self._gdbval.cast(_type_char_ptr()) + _managed_dict_offset() - char_ptr = ptr.cast(_type_char_ptr().pointer()).dereference() - if (int(char_ptr) & 1) == 0: + obj_ptr = self._gdbval.cast(_type_char_ptr()) + dict_ptr_ptr = obj_ptr + _managed_dict_offset() + dict_ptr = dict_ptr_ptr.cast(_type_char_ptr().pointer()).dereference() + if int(dict_ptr): return None - char_ptr += 1 + char_ptr = obj_ptr + _sizeof_pyobject() values_ptr = char_ptr.cast(gdb.lookup_type("PyDictValues").pointer()) values = values_ptr['values'] return PyKeysValuesPair(self.get_cached_keys(), values) diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 8dc590b4b89a..f7ed98ff6045 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -394,7 +394,7 @@ class Stats: return result def get_object_stats(self) -> dict[str, tuple[int, int]]: - total_materializations = self._data.get("Object new values", 0) + total_materializations = self._data.get("Object inline values", 0) total_allocations = self._data.get("Object allocations", 0) + self._data.get( "Object allocations from freelist", 0 ) @@ -1094,8 +1094,7 @@ def object_stats_section() -> Section: Below, "allocations" means "allocations that are not from a freelist". Total allocations = "Allocations from freelist" + "Allocations". - "New values" is the number of values arrays created for objects with - managed dicts. + "Inline values" is the number of values arrays inlined into objects. The cache hit/miss numbers are for the MRO cache, split into dunder and other names. -- 2.47.3