summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-10-13 14:19:34 +0100
committerGitHub <noreply@github.com>2021-10-13 14:19:34 +0100
commita8b9350964f43cb648c98c179c8037fbf3ff8a7d (patch)
tree13a539432c9d48ac278d34d040f17a7a12eac771 /Lib
parent97308dfcdc0696e0b116c37386e2ff4d72e6c3f4 (diff)
downloadcpython-git-a8b9350964f43cb648c98c179c8037fbf3ff8a7d.tar.gz
bpo-45340: Don't create object dictionaries unless actually needed (GH-28802)
* Never change types' cached keys. It could invalidate inline attribute objects. * Lazily create object dictionaries. * Update specialization of LOAD/STORE_ATTR. * Don't update shared keys version for deletion of value. * Update gdb support to handle instance values. * Rename SPLIT_KEYS opcodes to INSTANCE_VALUE.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/opcode.py5
-rw-r--r--Lib/test/test_descr.py10
-rw-r--r--Lib/test/test_dict.py5
-rw-r--r--Lib/test/test_gc.py29
-rw-r--r--Lib/test/test_sys.py10
5 files changed, 30 insertions, 29 deletions
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 5d35674688..efd6aefccc 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -231,7 +231,7 @@ _specialized_instructions = [
"BINARY_SUBSCR_DICT",
"JUMP_ABSOLUTE_QUICK",
"LOAD_ATTR_ADAPTIVE",
- "LOAD_ATTR_SPLIT_KEYS",
+ "LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_WITH_HINT",
"LOAD_ATTR_SLOT",
"LOAD_ATTR_MODULE",
@@ -242,8 +242,9 @@ _specialized_instructions = [
"LOAD_METHOD_CACHED",
"LOAD_METHOD_CLASS",
"LOAD_METHOD_MODULE",
+ "LOAD_METHOD_NO_DICT",
"STORE_ATTR_ADAPTIVE",
- "STORE_ATTR_SPLIT_KEYS",
+ "STORE_ATTR_INSTANCE_VALUE",
"STORE_ATTR_SLOT",
"STORE_ATTR_WITH_HINT",
# Super instructions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index af7848c0b1..a5404b30d2 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -5500,17 +5500,19 @@ class SharedKeyTests(unittest.TestCase):
class B(A):
pass
+ #Shrink keys by repeatedly creating instances
+ [(A(), B()) for _ in range(20)]
+
a, b = A(), B()
self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b)))
self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({"a":1}))
- # Initial hash table can contain at most 5 elements.
+ # Initial hash table can contain only one or two elements.
# Set 6 attributes to cause internal resizing.
a.x, a.y, a.z, a.w, a.v, a.u = range(6)
self.assertNotEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b)))
a2 = A()
- self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(a2)))
- self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({"a":1}))
- b.u, b.v, b.w, b.t, b.s, b.r = range(6)
+ self.assertGreater(sys.getsizeof(vars(a)), sys.getsizeof(vars(a2)))
+ self.assertLess(sys.getsizeof(vars(a2)), sys.getsizeof({"a":1}))
self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({"a":1}))
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 40143757fd..b43c83abd0 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -994,8 +994,8 @@ class DictTest(unittest.TestCase):
@support.cpython_only
def test_splittable_setdefault(self):
- """split table must be combined when setdefault()
- breaks insertion order"""
+ """split table must keep correct insertion
+ order when attributes are adding using setdefault()"""
a, b = self.make_shared_key_dict(2)
a['a'] = 1
@@ -1005,7 +1005,6 @@ class DictTest(unittest.TestCase):
size_b = sys.getsizeof(b)
b['a'] = 1
- self.assertGreater(size_b, size_a)
self.assertEqual(list(a), ['x', 'y', 'z', 'a', 'b'])
self.assertEqual(list(b), ['x', 'y', 'z', 'b', 'a'])
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 6c28b2b677..52948f1c7b 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -444,7 +444,7 @@ class GCTests(unittest.TestCase):
# 0, thus mutating the trash graph as a side effect of merely asking
# whether __del__ exists. This used to (before 2.3b1) crash Python.
# Now __getattr__ isn't called.
- self.assertEqual(gc.collect(), 4)
+ self.assertEqual(gc.collect(), 2)
self.assertEqual(len(gc.garbage), garbagelen)
def test_boom2(self):
@@ -471,7 +471,7 @@ class GCTests(unittest.TestCase):
# there isn't a second time, so this simply cleans up the trash cycle.
# We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
# reclaimed this way.
- self.assertEqual(gc.collect(), 4)
+ self.assertEqual(gc.collect(), 2)
self.assertEqual(len(gc.garbage), garbagelen)
def test_boom_new(self):
@@ -491,7 +491,7 @@ class GCTests(unittest.TestCase):
gc.collect()
garbagelen = len(gc.garbage)
del a, b
- self.assertEqual(gc.collect(), 4)
+ self.assertEqual(gc.collect(), 2)
self.assertEqual(len(gc.garbage), garbagelen)
def test_boom2_new(self):
@@ -513,7 +513,7 @@ class GCTests(unittest.TestCase):
gc.collect()
garbagelen = len(gc.garbage)
del a, b
- self.assertEqual(gc.collect(), 4)
+ self.assertEqual(gc.collect(), 2)
self.assertEqual(len(gc.garbage), garbagelen)
def test_get_referents(self):
@@ -943,8 +943,8 @@ class GCTests(unittest.TestCase):
A()
t = gc.collect()
c, nc = getstats()
- self.assertEqual(t, 2*N) # instance object & its dict
- self.assertEqual(c - oldc, 2*N)
+ self.assertEqual(t, N) # instance objects
+ self.assertEqual(c - oldc, N)
self.assertEqual(nc - oldnc, 0)
# But Z() is not actually collected.
@@ -964,8 +964,8 @@ class GCTests(unittest.TestCase):
Z()
t = gc.collect()
c, nc = getstats()
- self.assertEqual(t, 2*N)
- self.assertEqual(c - oldc, 2*N)
+ self.assertEqual(t, N)
+ self.assertEqual(c - oldc, N)
self.assertEqual(nc - oldnc, 0)
# The A() trash should have been reclaimed already but the
@@ -974,8 +974,8 @@ class GCTests(unittest.TestCase):
zs.clear()
t = gc.collect()
c, nc = getstats()
- self.assertEqual(t, 4)
- self.assertEqual(c - oldc, 4)
+ self.assertEqual(t, 2)
+ self.assertEqual(c - oldc, 2)
self.assertEqual(nc - oldnc, 0)
gc.enable()
@@ -1128,8 +1128,7 @@ class GCCallbackTests(unittest.TestCase):
@cpython_only
def test_collect_garbage(self):
self.preclean()
- # Each of these cause four objects to be garbage: Two
- # Uncollectables and their instance dicts.
+ # Each of these cause two objects to be garbage:
Uncollectable()
Uncollectable()
C1055820(666)
@@ -1138,8 +1137,8 @@ class GCCallbackTests(unittest.TestCase):
if v[1] != "stop":
continue
info = v[2]
- self.assertEqual(info["collected"], 2)
- self.assertEqual(info["uncollectable"], 8)
+ self.assertEqual(info["collected"], 1)
+ self.assertEqual(info["uncollectable"], 4)
# We should now have the Uncollectables in gc.garbage
self.assertEqual(len(gc.garbage), 4)
@@ -1156,7 +1155,7 @@ class GCCallbackTests(unittest.TestCase):
continue
info = v[2]
self.assertEqual(info["collected"], 0)
- self.assertEqual(info["uncollectable"], 4)
+ self.assertEqual(info["uncollectable"], 2)
# Uncollectables should be gone
self.assertEqual(len(gc.garbage), 0)
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 93e39bc183..2ce40fcde9 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1409,7 +1409,7 @@ class SizeofTest(unittest.TestCase):
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
- fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
+ fmt = 'P2nPI13Pl4Pn9Pn12PIPP'
s = vsize(fmt)
check(int, s)
# class
@@ -1422,15 +1422,15 @@ class SizeofTest(unittest.TestCase):
'5P')
class newstyleclass(object): pass
# Separate block for PyDictKeysObject with 8 keys and 5 entries
- check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + 5*calcsize("n2P"))
+ check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 32 + 21*calcsize("n2P"))
# dict with shared keys
- check(newstyleclass().__dict__, size('nQ2P') + 5*self.P)
+ check(newstyleclass().__dict__, size('nQ2P') + 15*self.P)
o = newstyleclass()
o.a = o.b = o.c = o.d = o.e = o.f = o.g = o.h = 1
# Separate block for PyDictKeysObject with 16 keys and 10 entries
- check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + 10*calcsize("n2P"))
+ check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 32 + 21*calcsize("n2P"))
# dict with shared keys
- check(newstyleclass().__dict__, size('nQ2P') + 10*self.P)
+ check(newstyleclass().__dict__, size('nQ2P') + 13*self.P)
# unicode
# each tuple contains a string and its expected character size
# don't put any static strings here, as they may contain