Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4f2f3b6

Browse files
committed
Account for shared keys in type's __sizeof__ (#13903).
1 parent f0c10f0 commit 4f2f3b6

4 files changed

Lines changed: 40 additions & 14 deletions

File tree

Include/dictobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash);
7575
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
7676
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
7777
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
78+
Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys);
7879
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
7980

8081
PyAPI_FUNC(int) PyDict_ClearFreeList(void);

Lib/test/test_sys.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,13 +829,19 @@ def delx(self): del self.__x
829829
check((), size(vh))
830830
check((1,2,3), size(vh) + 3*self.P)
831831
# type
832-
# (PyTypeObject + PyNumberMethods + PyMappingMethods +
833-
# PySequenceMethods + PyBufferProcs)
834-
s = size(vh + 'P2P15Pl4PP9PP11PIP') + size('16Pi17P 3P 10P 2P 3P')
832+
# static type: PyTypeObject
833+
s = size(vh + 'P2P15Pl4PP9PP11PI')
835834
check(int, s)
835+
# (PyTypeObject + PyNumberMethods + PyMappingMethods +
836+
# PySequenceMethods + PyBufferProcs + 4P)
837+
s = size(vh + 'P2P15Pl4PP9PP11PI') + size('34P 3P 10P 2P 4P')
838+
# Separate block for PyDictKeysObject with 4 entries
839+
s += size("PPPP") + 4*size("PPP")
836840
# class
837841
class newstyleclass(object): pass
838842
check(newstyleclass, s)
843+
# dict with shared keys
844+
check(newstyleclass().__dict__, size(h+"PPP4P"))
839845
# unicode
840846
# each tuple contains a string and its expected character size
841847
# don't put any static strings here, as they may contain

Objects/dictobject.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,22 +2398,23 @@ static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
23982398
static PyObject *
23992399
dict_sizeof(PyDictObject *mp)
24002400
{
2401-
Py_ssize_t size;
2402-
double res, keys_size;
2401+
Py_ssize_t size, res;
24032402

24042403
size = DK_SIZE(mp->ma_keys);
24052404
res = sizeof(PyDictObject);
24062405
if (mp->ma_values)
24072406
res += size * sizeof(PyObject*);
2408-
/* Count our share of the keys object -- with rounding errors. */
2409-
keys_size = sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
2410-
/* If refcnt > 1, then one count is (probably) held by a type */
2411-
/* XXX This is somewhat approximate :) */
2412-
if (mp->ma_keys->dk_refcnt < 3)
2413-
res += keys_size;
2414-
else
2415-
res += keys_size / (mp->ma_keys->dk_refcnt - 1);
2416-
return PyFloat_FromDouble(res);
2407+
/* If the dictionary is split, the keys portion is accounted-for
2408+
in the type object. */
2409+
if (mp->ma_keys->dk_refcnt == 1)
2410+
res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
2411+
return PyLong_FromSsize_t(res);
2412+
}
2413+
2414+
Py_ssize_t
2415+
_PyDict_KeysSize(PyDictKeysObject *keys)
2416+
{
2417+
return sizeof(PyDictKeysObject) + (DK_SIZE(keys)-1) * sizeof(PyDictKeyEntry);
24172418
}
24182419

24192420
PyDoc_STRVAR(contains__doc__,

Objects/typeobject.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,6 +2730,22 @@ type_dir(PyObject *self, PyObject *args)
27302730
return result;
27312731
}
27322732

2733+
static PyObject*
2734+
type_sizeof(PyObject *self, PyObject *args_unused)
2735+
{
2736+
Py_ssize_t size;
2737+
PyTypeObject *type = (PyTypeObject*)self;
2738+
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
2739+
PyHeapTypeObject* et = (PyHeapTypeObject*)type;
2740+
size = sizeof(PyHeapTypeObject);
2741+
if (et->ht_cached_keys)
2742+
size += _PyDict_KeysSize(et->ht_cached_keys);
2743+
}
2744+
else
2745+
size = sizeof(PyTypeObject);
2746+
return PyLong_FromSsize_t(size);
2747+
}
2748+
27332749
static PyMethodDef type_methods[] = {
27342750
{"mro", (PyCFunction)mro_external, METH_NOARGS,
27352751
PyDoc_STR("mro() -> list\nreturn a type's method resolution order")},
@@ -2745,6 +2761,8 @@ static PyMethodDef type_methods[] = {
27452761
PyDoc_STR("__subclasscheck__() -> bool\ncheck if a class is a subclass")},
27462762
{"__dir__", type_dir, METH_NOARGS,
27472763
PyDoc_STR("__dir__() -> list\nspecialized __dir__ implementation for types")},
2764+
{"__sizeof__", type_sizeof, METH_NOARGS,
2765+
"__sizeof__() -> int\nreturn memory consumption of the type object"},
27482766
{0}
27492767
};
27502768

0 commit comments

Comments
 (0)