8000 GH-95245: Store object values and dict pointers in single tagged poin… · python/cpython@de388c0 · GitHub
[go: up one dir, main page]

Skip to content

Commit de388c0

Browse files
authored
GH-95245: Store object values and dict pointers in single tagged pointer. (GH-95278)
1 parent fb75d01 commit de388c0

File tree

10 files changed

+269
-201
lines changed

10 files changed

+269
-201
lines changed

Include/cpython/dictobject.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,3 @@ typedef struct {
8383

8484
PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *);
8585
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
86-
87-
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg);
88-
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *self);

Include/cpython/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,7 @@ Py_DEPRECATED(3.11) typedef int UsingDeprecatedTrashcanMacro;
510510
#define Py_TRASHCAN_SAFE_END(op) \
511511
Py_TRASHCAN_END; \
512512
} while(0);
513+
514+
515+
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
516+
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj);

Include/internal/pycore_object.h

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,24 +274,49 @@ extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
274274
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
275275
PyObject *name);
276276

277-
static inline PyDictValues **_PyObject_ValuesPointer(PyObject *obj)
277+
typedef union {
278+
PyObject *dict;
279+
/* Use a char* to generate a warning if directly assigning a PyDictValues */
280+
char *values;
281+
} PyDictOrValues;
282+
283+
static inline PyDictOrValues *
284+
_PyObject_DictOrValuesPointer(PyObject *obj)
278285
{
279286
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
280-
return ((PyDictValues **)obj)-4;
287+
return ((PyDictOrValues *)obj)-3;
281288
}
282289

283-
static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj)
290+
static inline int
291+
_PyDictOrValues_IsValues(PyDictOrValues dorv)
284292
{
285-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
286-
return ((PyObject **)obj)-3;
293+
return ((uintptr_t)dorv.values) & 1;
294+
}
295+
296+
static inline PyDictValues *
297+
_PyDictOrValues_GetValues(PyDictOrValues dorv)
298+
{
299+
assert(_PyDictOrValues_IsValues(dorv));
300+
return (PyDictValues *)(dorv.values + 1);
301+
}
302+
303+
static inline PyObject *
304+
_PyDictOrValues_GetDict(PyDictOrValues dorv)
305+
{
306+
assert(!_PyDictOrValues_IsValues(dorv));
307+
return dorv.dict;
308+
}
309+
310+
static inline void
311+
_PyDictOrValues_SetValues(PyDictOrValues *ptr, PyDictValues *values)
312+
{
313+
ptr->values = ((char *)values) - 1;
287314
}
288315

289316
#define MANAGED_DICT_OFFSET (((int)sizeof(PyObject *))*-3)
290317

291-
extern PyObject ** _PyObject_DictPointer(PyObject *);
292-
extern int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg);
293-
extern void _PyObject_ClearInstanceAttributes(PyObject *self);
294-
extern void _PyObject_FreeInstanceAttributes(PyObject *self);
318+
extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
319+
extern void _PyObject_FreeInstanceAttributes(PyObject *obj);
295320
extern int _PyObject_IsInstanceDictEmpty(PyObject *);
296321
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
297322

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Merge managed dict and values pointer into a single tagged pointer to save
2+
one word in the pre-header.

Objects/dictobject.c

Lines changed: 90 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,7 +5368,7 @@ init_inline_values(PyObject *obj, PyTypeObject *tp)
53685368
for (int i = 0; i < size; i++) {
53695369
values->values[i] = NULL;
53705370
}
5371-
*_PyObject_ValuesPointer(obj) = values;
5371+
_PyDictOrValues_SetValues(_PyObject_DictOrValuesPointer(obj), values);
53725372
return 0;
53735373
}
53745374

@@ -5394,7 +5394,7 @@ _PyObject_InitializeDict(PyObject *obj)
53945394
if (dict == NULL) {
53955395
return -1;
53965396
}
5397-
PyObject **dictptr = _PyObject_DictPointer(obj);
5397+
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
53985398
*dictptr = dict;
53995399
return 0;
54005400
}
@@ -5422,7 +5422,6 @@ make_dict_from_instance_attributes(PyDictKeysObject *keys, PyDictValues *values)
54225422
PyObject *
54235423
_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values)
54245424
{
5425-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
54265425
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
54275426
OBJECT_STAT_INC(dict_materialized_on_request);
54285427
return make_dict_from_instance_attributes(keys, values);
@@ -5458,8 +5457,7 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
54585457
if (dict == NULL) {
54595458
return -1;
54605459
}
5461-
*_PyObject_ValuesPointer(obj) = NULL;
5462-
*_PyObject_ManagedDictPointer(obj) = dict;
5460+
_PyObject_DictOrValuesPointer(obj)->dict = dict;
54635461
if (value == NULL) {
54645462
return PyDict_DelItem(dict, name);
54655463
}
@@ -5488,6 +5486,37 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
54885486
return 0;
54895487
}
54905488

5489+
/* Sanity check for managed dicts */
5490+
#if 0
5491+
#define CHECK(val) assert(val); if (!(val)) { return 0; }
5492+
5493+
int
5494+
_PyObject_ManagedDictValidityCheck(PyObject *obj)
5495+
{
5496+
PyTypeObject *tp = Py_TYPE(obj);
5497+
CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
5498+
PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
5499+
if (_PyDictOrValues_IsValues(*dorv_ptr)) {
5500+
PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
5501+
int size = ((uint8_t *)values)[-2];
5502+
int count = 0;
5503+
PyDictKeysObject *keys = CACHED_KEYS(tp);
5504+
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5505+
if (values->values[i] != NULL) {
5506+
count++;
5507+
}
5508+
}
5509+
CHECK(size == count);
5510+
}
5511+
else {
5512+
if (dorv_ptr->dict != NULL) {
5513+
CHECK(PyDict_Check(dorv_ptr->dict));
5514+
}
5515+
}
5516+
return 1;
5517+
}
5518+
#endif
5519+
54915520
PyObject *
54925521
_PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
54935522
PyObject *name)
@@ -5511,105 +5540,94 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
55115540
if (tp->tp_dictoffset == 0) {
55125541
return 1;
55135542
}
5514-
PyObject **dictptr;
5543+
PyObject *dict;
55155544
if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
5516-
PyDictValues *values = *_PyObject_ValuesPointer(obj);
5517-
if (values) {
5545+
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
5546+
if (_PyDictOrValues_IsValues(dorv)) {
55185547
PyDictKeysObject *keys = CACHED_KEYS(tp);
55195548
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5520-
if (values->values[i] != NULL) {
5549+
if (_PyDictOrValues_GetValues(dorv)->values[i] != NULL) {
55215550
return 0;
55225551
}
55235552
}
55245553
return 1;
55255554
}
5526-
dictptr = _PyObject_ManagedDictPointer(obj);
5555+
dict = _PyDictOrValues_GetDict(dorv);
55275556
}
55285557
else {
5529-
dictptr = _PyObject_DictPointer(obj);
5558+
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
5559+
dict = *dictptr;
55305560
}
5531-
PyObject *dict = *dictptr;
55325561
if (dict == NULL) {
55335562
return 1;
55345563
}
55355564
return ((PyDictObject *)dict)->ma_used == 0;
55365565
}
55375566

5538-
5539-
int
5540-
_PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg)
5541-
{
5542-
PyTypeObject *tp = Py_TYPE(self);
5543-
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
5544-
PyDictValues **values_ptr = _PyObject_ValuesPointer(self);
5545-
if (*values_ptr == NULL) {
5546-
return 0;
5547-
}
5548-
PyDictKeysObject *keys = CACHED_KEYS(tp);
5549-
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5550-
Py_VISIT((*values_ptr)->values[i]);
5551-
}
5552-
return 0;
5553-
}
5554-
5555-
void
5556-
_PyObject_ClearInstanceAttributes(PyObject *self)
5557-
{
5558-
PyTypeObject *tp = Py_TYPE(self);
5559-
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
5560-
PyDictValues **values_ptr = _PyObject_ValuesPointer(self);
5561-
if (*values_ptr == NULL) {
5562-
return;
5563-
}
5564-
PyDictKeysObject *keys = CACHED_KEYS(tp);
5565-
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5566-
Py_CLEAR((*values_ptr)->values[i]);
5567-
}
5568-
}
5569-
55705567
void
55715568
_PyObject_FreeInstanceAttributes(PyObject *self)
55725569
{
55735570
PyTypeObject *tp = Py_TYPE(self);
55745571
assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
5575-
PyDictValues **values_ptr = _PyObject_ValuesPointer(self);
5576-
if (*values_ptr == NULL) {
5572+
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
5573+
if (!_PyDictOrValues_IsValues(dorv)) {
55775574
return;
55785575
}
5576+
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
55795577
PyDictKeysObject *keys = CACHED_KEYS(tp);
55805578
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5581-
Py_XDECREF((*values_ptr)->values[i]);
5579+
Py_XDECREF(values->values[i]);
55825580
}
5583-
free_values(*values_ptr);
5581+
free_values(values);
55845582
}
55855583

55865584
int
5587-
_PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg)
5585+
_PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
55885586
{
5589-
PyTypeObject *tp = Py_TYPE(self);
5587+
PyTypeObject *tp = Py_TYPE(obj);
55905588
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
55915589
return 0;
55925590
}
55935591
assert(tp->tp_dictoffset);
5594-
int err = _PyObject_VisitInstanceAttributes(self, visit, arg);
5595-
if (err) {
5596-
return err;
5592+
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
5593+
if (_PyDictOrValues_IsValues(dorv)) {
5594+
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
5595+
PyDictKeysObject *keys = CACHED_KEYS(tp);
5596+
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5597+
Py_VISIT(values->values[i]);
5598+
}
5599+
}
5600+
else {
5601+
PyObject *dict = _PyDictOrValues_GetDict(dorv);
5602+
Py_VISIT(dict);
55975603
}
5598-
Py_VISIT(*_PyObject_ManagedDictPointer(self));
55995604
return 0;
56005605
}
56015606

5602-
56035607
void
5604-
_PyObject_ClearManagedDict(PyObject *self)
5608+
_PyObject_ClearManagedDict(PyObject *obj)
56055609
{
5606-
PyTypeObject *tp = Py_TYPE(self);
5610+
PyTypeObject *tp = Py_TYPE(obj);
56075611
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
56085612
return;
56095613
}
5610-
_PyObject_FreeInstanceAttributes(self);
5611-
*_PyObject_ValuesPointer(self) = NULL;
5612-
Py_CLEAR(*_PyObject_ManagedDictPointer(self));
5614+
PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
5615+
if (_PyDictOrValues_IsValues(*dorv_ptr)) {
5616+
PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
5617+
PyDictKeysObject *keys = CACHED_KEYS(tp);
5618+
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
5619+
Py_CLEAR(values->values[i]);
5620+
}
5621+
dorv_ptr->dict = NULL;
5622+
free_values(values);
5623+
}
5624+
else {
5625+
PyObject *dict = dorv_ptr->dict;
5626+
if (dict) {
5627+
dorv_ptr->dict = NULL;
5628+
Py_DECREF(dict);
5629+
}
5630+
}
56135631
}
56145632

56155633
PyObject *
@@ -5618,25 +5636,26 @@ PyObject_GenericGetDict(PyObject *obj, void *context)
56185636
PyObject *dict;
56195637
PyTypeObject *tp = Py_TYPE(obj);
56205638
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
5621-
PyDictValues **values_ptr = _PyObject_ValuesPointer(obj);
5622-
PyObject **dictptr = _PyObject_ManagedDictPointer(obj);
5623-
if (*values_ptr) {
5624-
assert(*dictptr == NULL);
5639+
PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
5640+
if (_PyDictOrValues_IsValues(*dorv_ptr)) {
5641+
PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
56255642
OBJECT_STAT_INC(dict_materialized_on_request);
5626-
*dictptr = dict = make_dict_from_instance_attributes(CACHED_KEYS(tp), *values_ptr);
5643+
dict = make_dict_from_instance_attributes(CACHED_KEYS(tp), values);
56275644
if (dict != NULL) {
5628-
*values_ptr = NULL;
5645+
dorv_ptr->dict = dict;
56295646
}
56305647
}
5631-
else if (*dictptr == NULL) {
5632-
*dictptr = dict = PyDict_New();
5633-
}
56345648
else {
5635-
dict = *dictptr;
5649+
dict = _PyDictOrValues_GetDict(*dorv_ptr);
5650+
if (dict == NULL) {
5651+
dictkeys_incref(CACHED_KEYS(tp));
5652+
dict = new_dict_with_shared_keys(CACHED_KEYS(tp));
5653+
dorv_ptr->dict = dict;
5654+
}
56365655
}
56375656
}
56385657
else {
5639-
PyObject **dictptr = _PyObject_DictPointer(obj);
5658+
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
56405659
if (dictptr == NULL) {
56415660
PyErr_SetString(PyExc_AttributeError,
56425661
"This object has no __dict__");

0 commit comments

Comments
 (0)
0