Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: typed-cells Changeset: r81814:d4fbc7fa07b0 Date: 2016-01-15 19:39 +0100 http://bitbucket.org/pypy/pypy/changeset/d4fbc7fa07b0/
Log: slightly different approach for integrating mutable cells and immutability: before, an integer field went from storing a W_IntObject immutably, then switching to a IntMutableCell on the first mutation. This makes everything less type stable, leading to more bridges and making this branch fundamentally incompatible with heap profiling. Now the IntMutableCell is *always* stored, but if it is ever mutated map.ever_mutated is set to True. That means that as long as ever_mutated is False, it's safe to fold the read from the IntMutableCell away as well, in an @elidable method. diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py --- a/pypy/objspace/std/mapdict.py +++ b/pypy/objspace/std/mapdict.py @@ -36,19 +36,15 @@ if attr is None: return self.terminator._read_terminator(obj, selector) if ( - jit.isconstant(attr.storageindex) and + jit.isconstant(attr) and jit.isconstant(obj) and not attr.ever_mutated ): - result = self._pure_mapdict_read_storage(obj, attr.storageindex) + result = attr._pure_read(obj) else: result = obj._mapdict_read_storage(attr.storageindex) return attr._read_cell(result) - @jit.elidable - def _pure_mapdict_read_storage(self, obj, storageindex): - return obj._mapdict_read_storage(storageindex) - def write(self, obj, selector, w_value): attr = self.find_map_attr(selector) if attr is None: @@ -176,6 +172,8 @@ # the order is important here: first change the map, then the storage, # for the benefit of the special subclasses obj._set_mapdict_map(attr) + w_value = attr._write_cell(None, w_value) + assert w_value is not None obj._mapdict_write_storage(attr.storageindex, w_value) def materialize_r_dict(self, space, obj, dict_w): @@ -298,6 +296,13 @@ # if the flag is False, we don't need to unbox the attribute. self.can_contain_mutable_cell = False + @jit.elidable + def _pure_read(self, obj): + # this is safe even if the mapdict stores a mutable cell. the cell can + # only be changed is ever_mutated is set to True + result = obj._mapdict_read_storage(self.storageindex) + return self._read_cell(result) + def _read_cell(self, w_cell): if not self.can_contain_mutable_cell: return w_cell @@ -313,16 +318,14 @@ return None check = self._ensure_can_contain_mutable_cell() assert check - if self.ever_mutated: - return IntMutableCell(w_value.intval) + return IntMutableCell(w_value.intval) if type(w_value) is W_FloatObject: if isinstance(w_cell, FloatMutableCell): w_cell.floatvalue = w_value.floatval return None check = self._ensure_can_contain_mutable_cell() assert check - if self.ever_mutated: - return FloatMutableCell(w_value.floatval) + return FloatMutableCell(w_value.floatval) return w_value @jit.elidable diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py --- a/pypy/objspace/std/test/test_mapdict.py +++ b/pypy/objspace/std/test/test_mapdict.py @@ -108,23 +108,27 @@ assert obj2.map is obj.map def test_attr_immutability(monkeypatch): + from pypy.objspace.std.intobject import W_IntObject cls = Class() obj = cls.instantiate() - obj.setdictvalue(space, "a", 10) - obj.setdictvalue(space, "b", 20) - obj.setdictvalue(space, "b", 30) - assert obj.storage == [10, 30] + obj.setdictvalue(space, "a", W_IntObject(10)) + obj.setdictvalue(space, "b", W_IntObject(20)) + obj.setdictvalue(space, "b", W_IntObject(30)) + mutcella, mutcellb = obj.storage + assert mutcella.intvalue == 10 + assert mutcellb.intvalue == 30 assert obj.map.ever_mutated == True assert obj.map.back.ever_mutated == False indices = [] + orig_pure_read = PlainAttribute._pure_read - def _pure_mapdict_read_storage(obj, storageindex): - assert storageindex == 0 - indices.append(storageindex) - return obj._mapdict_read_storage(storageindex) + def _pure_read(self, obj): + assert self.storageindex == 0 + indices.append(self.storageindex) + return orig_pure_read(self, obj) - obj.map._pure_mapdict_read_storage = _pure_mapdict_read_storage + monkeypatch.setattr(PlainAttribute, "_pure_read", _pure_read) monkeypatch.setattr(jit, "isconstant", lambda c: True) assert obj.getdictvalue(space, "a") == 10 @@ -133,16 +137,20 @@ assert indices == [0, 0] obj2 = cls.instantiate() - obj2.setdictvalue(space, "a", 15) - obj2.setdictvalue(space, "b", 25) + obj2.setdictvalue(space, "a", W_IntObject(15)) + obj2.setdictvalue(space, "b", W_IntObject(25)) + mutcella, mutcellb = obj2.storage assert obj2.map is obj.map assert obj2.map.ever_mutated == True assert obj2.map.back.ever_mutated == False # mutating obj2 changes the map - obj2.setdictvalue(space, "a", 50) + obj2.setdictvalue(space, "a", W_IntObject(50)) assert obj2.map.back.ever_mutated == True assert obj2.map is obj.map + assert obj2.storage[0] is mutcella + assert obj2.storage[1] is mutcellb + def test_attr_immutability_delete(): cls = Class() @@ -154,6 +162,21 @@ assert obj.map.ever_mutated == True assert obj.map is map1 +def test_immutable_with_mutcell(): + # even an immutable attribute will be stored as a mutcell. The reason is + # that then the type of the attribute is more predictable (eg always + # IntMutableCell and sometimes IntMutableCell and sometimes W_IntObject) + from pypy.objspace.std.intobject import W_IntObject + cls = Class() + obj = cls.instantiate() + # make sure the attribute counts as mutable + obj.setdictvalue(space, "a", W_IntObject(4)) + # not wrapped because of the FakeSpace :-( + assert obj.getdictvalue(space, "a") == 4 + mutcell = obj._mapdict_read_storage(0) + assert mutcell.intvalue == 4 + + def test_mutcell_not_immutable(): from pypy.objspace.std.intobject import W_IntObject cls = Class() @@ -211,19 +234,6 @@ assert mutcell2 is mutcell1 -def test_no_mutcell_if_immutable(): - # don't introduce an immutable cell if the attribute seems immutable - from pypy.objspace.std.intobject import W_IntObject - cls = Class() - obj = cls.instantiate() - obj.setdictvalue(space, "a", W_IntObject(5)) - assert not obj.map.ever_mutated - - assert obj.getdictvalue(space, "a").intval == 5 - mutcell = obj._mapdict_read_storage(0) - assert mutcell.intval == 5 - - def test_mutcell_unwrap_only_if_needed(): from pypy.objspace.std.intobject import W_IntObject cls = Class() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit