Account for shared keys in type's __sizeof__ (#13903).

This commit is contained in:
Martin v. Loewis 2012-04-24 19:13:57 +02:00
parent f0c10f0840
commit 4f2f3b6217
4 changed files with 40 additions and 14 deletions

View File

@ -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);

View File

@ -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

View File

@ -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__,

View File

@ -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}
}; };