[IMP] fields: reduce memory footprint of list/set field attributes
The optimization consists in using tuples for attributes `inverse_fields`, `computed_fields` and `_triggers`, and to let them share their value when it is empty, which is common. This saves around 1.8Mb per registry.
This commit is contained in:
parent
3df7754087
commit
ffa7f28d34
|
@ -268,7 +268,6 @@ class Field(object):
|
|||
relational = False # whether the field is a relational one
|
||||
model_name = None # name of the model of this field
|
||||
comodel_name = None # name of the model of values (if relational)
|
||||
inverse_fields = None # list of inverse fields (objects)
|
||||
|
||||
store = True # whether the field is stored in database
|
||||
index = False # whether the field is indexed in database
|
||||
|
@ -294,18 +293,15 @@ class Field(object):
|
|||
change_default = None # whether the field may trigger a "user-onchange"
|
||||
deprecated = None # whether the field is ... deprecated
|
||||
|
||||
inverse_fields = () # collection of inverse fields (objects)
|
||||
computed_fields = () # fields computed with the same method as self
|
||||
_triggers = () # invalidation and recomputation triggers
|
||||
|
||||
def __init__(self, string=None, **kwargs):
|
||||
kwargs['string'] = string
|
||||
attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
|
||||
self._attrs = attrs or EMPTY_DICT
|
||||
|
||||
# self._triggers is a set of pairs (field, path) that represents the
|
||||
# computed fields that depend on `self`. When `self` is modified, it
|
||||
# invalidates the cache of each `field`, and registers the records to
|
||||
# recompute based on `path`. See method `modified` below for details.
|
||||
self._triggers = set()
|
||||
self.inverse_fields = []
|
||||
|
||||
def _set_attr(self, name, value):
|
||||
""" Set the given field attribute, and add it to `_attrs` if necessary. """
|
||||
object.__setattr__(self, name, value)
|
||||
|
@ -542,6 +538,16 @@ class Field(object):
|
|||
#
|
||||
# Setup of field triggers
|
||||
#
|
||||
# The triggers is a collection of pairs (field, path) of computed fields
|
||||
# that depend on `self`. When `self` is modified, it invalidates the cache
|
||||
# of each `field`, and registers the records to recompute based on `path`.
|
||||
# See method `modified` below for details.
|
||||
#
|
||||
|
||||
def add_trigger(self, trigger):
|
||||
""" Add a recomputation trigger on `self`. """
|
||||
if trigger not in self._triggers:
|
||||
self._triggers += (trigger,)
|
||||
|
||||
def setup_triggers(self, env):
|
||||
""" Add the necessary triggers to invalidate/recompute `self`. """
|
||||
|
@ -570,12 +576,12 @@ class Field(object):
|
|||
continue
|
||||
|
||||
#_logger.debug("Add trigger on %s to recompute %s", field, self)
|
||||
field._triggers.add((self, '.'.join(path0 or ['id'])))
|
||||
field.add_trigger((self, '.'.join(path0 or ['id'])))
|
||||
|
||||
# add trigger on inverse fields, too
|
||||
for invf in field.inverse_fields:
|
||||
#_logger.debug("Add trigger on %s to recompute %s", invf, self)
|
||||
invf._triggers.add((self, '.'.join(path0 + [head])))
|
||||
invf.add_trigger((self, '.'.join(path0 + [head])))
|
||||
|
||||
# recursively traverse the dependency
|
||||
if tail:
|
||||
|
@ -1684,8 +1690,8 @@ class One2many(_RelationalMulti):
|
|||
# (res_model/res_id pattern). Only inverse the field if this is
|
||||
# a `Many2one` field.
|
||||
if isinstance(invf, Many2one):
|
||||
self.inverse_fields.append(invf)
|
||||
invf.inverse_fields.append(self)
|
||||
self.inverse_fields += (invf,)
|
||||
invf.inverse_fields += (self,)
|
||||
|
||||
_description_relation_field = property(attrgetter('inverse_name'))
|
||||
|
||||
|
@ -1756,8 +1762,8 @@ class Many2many(_RelationalMulti):
|
|||
# if inverse field has already been setup, it is present in m2m
|
||||
invf = m2m.get((self.relation, self.column2, self.column1))
|
||||
if invf:
|
||||
self.inverse_fields.append(invf)
|
||||
invf.inverse_fields.append(self)
|
||||
self.inverse_fields += (invf,)
|
||||
invf.inverse_fields += (self,)
|
||||
else:
|
||||
# add self in m2m, so that its inverse field can find it
|
||||
m2m[(self.relation, self.column1, self.column2)] = self
|
||||
|
|
|
@ -2992,14 +2992,15 @@ class BaseModel(object):
|
|||
if column:
|
||||
cls._columns[name] = column
|
||||
|
||||
# group fields by compute to determine field.computed_fields
|
||||
fields_by_compute = defaultdict(list)
|
||||
# determine field.computed_fields
|
||||
computed_fields = defaultdict(list)
|
||||
for field in cls._fields.itervalues():
|
||||
if field.compute:
|
||||
field.computed_fields = fields_by_compute[field.compute]
|
||||
field.computed_fields.append(field)
|
||||
else:
|
||||
field.computed_fields = []
|
||||
computed_fields[field.compute].append(field)
|
||||
|
||||
for fields in computed_fields.itervalues():
|
||||
for field in fields:
|
||||
field.computed_fields = fields
|
||||
|
||||
@api.model
|
||||
def _setup_complete(self):
|
||||
|
@ -3017,7 +3018,7 @@ class BaseModel(object):
|
|||
model = self.env[model_name]
|
||||
for field_name in field_names:
|
||||
field = model._fields[field_name]
|
||||
field._triggers.update(triggers)
|
||||
map(field.add_trigger, triggers)
|
||||
|
||||
# determine old-api structures about inherited fields
|
||||
cls._inherits_reload()
|
||||
|
|
Loading…
Reference in New Issue