mirror of https://github.com/python/cpython
Account for shared keys in type's __sizeof__ (#13903).
This commit is contained in:
parent
f0c10f0840
commit
4f2f3b6217
|
@ -75,6 +75,7 @@ PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash);
|
||||||
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
|
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
|
||||||
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
|
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
|
||||||
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
|
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
|
||||||
|
Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys);
|
||||||
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
|
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyDict_ClearFreeList(void);
|
PyAPI_FUNC(int) PyDict_ClearFreeList(void);
|
||||||
|
|
|
@ -829,13 +829,19 @@ class SizeofTest(unittest.TestCase):
|
||||||
check((), size(vh))
|
check((), size(vh))
|
||||||
check((1,2,3), size(vh) + 3*self.P)
|
check((1,2,3), size(vh) + 3*self.P)
|
||||||
# type
|
# type
|
||||||
# (PyTypeObject + PyNumberMethods + PyMappingMethods +
|
# static type: PyTypeObject
|
||||||
# PySequenceMethods + PyBufferProcs)
|
s = size(vh + 'P2P15Pl4PP9PP11PI')
|
||||||
s = size(vh + 'P2P15Pl4PP9PP11PIP') + size('16Pi17P 3P 10P 2P 3P')
|
|
||||||
check(int, s)
|
check(int, s)
|
||||||
|
# (PyTypeObject + PyNumberMethods + PyMappingMethods +
|
||||||
|
# PySequenceMethods + PyBufferProcs + 4P)
|
||||||
|
s = size(vh + 'P2P15Pl4PP9PP11PI') + size('34P 3P 10P 2P 4P')
|
||||||
|
# Separate block for PyDictKeysObject with 4 entries
|
||||||
|
s += size("PPPP") + 4*size("PPP")
|
||||||
# class
|
# class
|
||||||
class newstyleclass(object): pass
|
class newstyleclass(object): pass
|
||||||
check(newstyleclass, s)
|
check(newstyleclass, s)
|
||||||
|
# dict with shared keys
|
||||||
|
check(newstyleclass().__dict__, size(h+"PPP4P"))
|
||||||
# unicode
|
# unicode
|
||||||
# each tuple contains a string and its expected character size
|
# each tuple contains a string and its expected character size
|
||||||
# don't put any static strings here, as they may contain
|
# don't put any static strings here, as they may contain
|
||||||
|
|
|
@ -2398,22 +2398,23 @@ static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dict_sizeof(PyDictObject *mp)
|
dict_sizeof(PyDictObject *mp)
|
||||||
{
|
{
|
||||||
Py_ssize_t size;
|
Py_ssize_t size, res;
|
||||||
double res, keys_size;
|
|
||||||
|
|
||||||
size = DK_SIZE(mp->ma_keys);
|
size = DK_SIZE(mp->ma_keys);
|
||||||
res = sizeof(PyDictObject);
|
res = sizeof(PyDictObject);
|
||||||
if (mp->ma_values)
|
if (mp->ma_values)
|
||||||
res += size * sizeof(PyObject*);
|
res += size * sizeof(PyObject*);
|
||||||
/* Count our share of the keys object -- with rounding errors. */
|
/* If the dictionary is split, the keys portion is accounted-for
|
||||||
keys_size = sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
|
in the type object. */
|
||||||
/* If refcnt > 1, then one count is (probably) held by a type */
|
if (mp->ma_keys->dk_refcnt == 1)
|
||||||
/* XXX This is somewhat approximate :) */
|
res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
|
||||||
if (mp->ma_keys->dk_refcnt < 3)
|
return PyLong_FromSsize_t(res);
|
||||||
res += keys_size;
|
}
|
||||||
else
|
|
||||||
res += keys_size / (mp->ma_keys->dk_refcnt - 1);
|
Py_ssize_t
|
||||||
return PyFloat_FromDouble(res);
|
_PyDict_KeysSize(PyDictKeysObject *keys)
|
||||||
|
{
|
||||||
|
return sizeof(PyDictKeysObject) + (DK_SIZE(keys)-1) * sizeof(PyDictKeyEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(contains__doc__,
|
PyDoc_STRVAR(contains__doc__,
|
||||||
|
|
|
@ -2730,6 +2730,22 @@ type_dir(PyObject *self, PyObject *args)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
type_sizeof(PyObject *self, PyObject *args_unused)
|
||||||
|
{
|
||||||
|
Py_ssize_t size;
|
||||||
|
PyTypeObject *type = (PyTypeObject*)self;
|
||||||
|
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||||
|
PyHeapTypeObject* et = (PyHeapTypeObject*)type;
|
||||||
|
size = sizeof(PyHeapTypeObject);
|
||||||
|
if (et->ht_cached_keys)
|
||||||
|
size += _PyDict_KeysSize(et->ht_cached_keys);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
size = sizeof(PyTypeObject);
|
||||||
|
return PyLong_FromSsize_t(size);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef type_methods[] = {
|
static PyMethodDef type_methods[] = {
|
||||||
{"mro", (PyCFunction)mro_external, METH_NOARGS,
|
{"mro", (PyCFunction)mro_external, METH_NOARGS,
|
||||||
PyDoc_STR("mro() -> list\nreturn a type's method resolution order")},
|
PyDoc_STR("mro() -> list\nreturn a type's method resolution order")},
|
||||||
|
@ -2745,6 +2761,8 @@ static PyMethodDef type_methods[] = {
|
||||||
PyDoc_STR("__subclasscheck__() -> bool\ncheck if a class is a subclass")},
|
PyDoc_STR("__subclasscheck__() -> bool\ncheck if a class is a subclass")},
|
||||||
{"__dir__", type_dir, METH_NOARGS,
|
{"__dir__", type_dir, METH_NOARGS,
|
||||||
PyDoc_STR("__dir__() -> list\nspecialized __dir__ implementation for types")},
|
PyDoc_STR("__dir__() -> list\nspecialized __dir__ implementation for types")},
|
||||||
|
{"__sizeof__", type_sizeof, METH_NOARGS,
|
||||||
|
"__sizeof__() -> int\nreturn memory consumption of the type object"},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue