diff -r 6c5f9c6c25ea Lib/test/test_dict.py --- a/Lib/test/test_dict.py Wed Sep 14 18:17:32 2016 +0300 +++ b/Lib/test/test_dict.py Thu Sep 15 05:05:54 2016 +0900 @@ -839,83 +839,6 @@ pass self._tracked(MyDict()) - def make_shared_key_dict(self, n): - class C: - pass - - dicts = [] - for i in range(n): - a = C() - a.x, a.y, a.z = 1, 2, 3 - dicts.append(a.__dict__) - - return dicts - - @support.cpython_only - def test_splittable_del(self): - """split table must be combined when del d[k]""" - a, b = self.make_shared_key_dict(2) - - orig_size = sys.getsizeof(a) - - del a['y'] # split table is combined - with self.assertRaises(KeyError): - del a['y'] - - self.assertGreater(sys.getsizeof(a), orig_size) - self.assertEqual(list(a), ['x', 'z']) - self.assertEqual(list(b), ['x', 'y', 'z']) - - # Two dicts have different insertion order. - a['y'] = 42 - self.assertEqual(list(a), ['x', 'z', 'y']) - self.assertEqual(list(b), ['x', 'y', 'z']) - - @support.cpython_only - def test_splittable_pop(self): - """split table must be combined when d.pop(k)""" - a, b = self.make_shared_key_dict(2) - - orig_size = sys.getsizeof(a) - - a.pop('y') # split table is combined - with self.assertRaises(KeyError): - a.pop('y') - - self.assertGreater(sys.getsizeof(a), orig_size) - self.assertEqual(list(a), ['x', 'z']) - self.assertEqual(list(b), ['x', 'y', 'z']) - - # Two dicts have different insertion order. - a['y'] = 42 - self.assertEqual(list(a), ['x', 'z', 'y']) - self.assertEqual(list(b), ['x', 'y', 'z']) - - @support.cpython_only - def test_splittable_pop_pending(self): - """pop a pending key in a splitted table should not crash""" - a, b = self.make_shared_key_dict(2) - - a['a'] = 4 - with self.assertRaises(KeyError): - b.pop('a') - - @support.cpython_only - def test_splittable_popitem(self): - """split table must be combined when d.popitem()""" - a, b = self.make_shared_key_dict(2) - - orig_size = sys.getsizeof(a) - - item = a.popitem() # split table is combined - self.assertEqual(item, ('z', 3)) - with self.assertRaises(KeyError): - del a['z'] - - self.assertGreater(sys.getsizeof(a), orig_size) - self.assertEqual(list(a), ['x', 'y']) - self.assertEqual(list(b), ['x', 'y', 'z']) - def test_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): data = {1:"a", 2:"b", 3:"c"} diff -r 6c5f9c6c25ea Objects/dictobject.c --- a/Objects/dictobject.c Wed Sep 14 18:17:32 2016 +0300 +++ b/Objects/dictobject.c Thu Sep 15 05:05:54 2016 +0900 @@ -388,7 +388,7 @@ * This can be used to reserve enough size to insert n entries without * resizing. */ -#define ESTIMATE_SIZE(n) (((n)*3) >> 1) +#define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) /* Alternative fraction that is otherwise close enough to 2n/3 to make * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. @@ -1236,16 +1236,16 @@ but can be resplit by make_keys_shared(). */ static int -dictresize(PyDictObject *mp, Py_ssize_t minused) +dictresize(PyDictObject *mp, Py_ssize_t minsize) { Py_ssize_t i, newsize; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *ep0; - /* Find the smallest table size > minused. */ + // Find the smallest table size >= minsize. for (newsize = PyDict_MINSIZE; - newsize <= minused && newsize > 0; + newsize < minsize && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { @@ -4229,9 +4229,11 @@ CACHED_KEYS(tp) = NULL; DK_DECREF(cached); } - } else { + } + else { + int shared = cached == ((PyDictObject *)dict)->ma_keys; res = PyDict_SetItem(dict, key, value); - if (cached != ((PyDictObject *)dict)->ma_keys) { + if (shared && cached != ((PyDictObject *)dict)->ma_keys) { /* Either update tp->ht_cached_keys or delete it */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict);