[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
|
relational = False # whether the field is a relational one
|
||||||
model_name = None # name of the model of this field
|
model_name = None # name of the model of this field
|
||||||
comodel_name = None # name of the model of values (if relational)
|
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
|
store = True # whether the field is stored in database
|
||||||
index = False # whether the field is indexed 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"
|
change_default = None # whether the field may trigger a "user-onchange"
|
||||||
deprecated = None # whether the field is ... deprecated
|
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):
|
def __init__(self, string=None, **kwargs):
|
||||||
kwargs['string'] = string
|
kwargs['string'] = string
|
||||||
attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
|
attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
|
||||||
self._attrs = attrs or EMPTY_DICT
|
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):
|
def _set_attr(self, name, value):
|
||||||
""" Set the given field attribute, and add it to `_attrs` if necessary. """
|
""" Set the given field attribute, and add it to `_attrs` if necessary. """
|
||||||
object.__setattr__(self, name, value)
|
object.__setattr__(self, name, value)
|
||||||
|
@ -542,6 +538,16 @@ class Field(object):
|
||||||
#
|
#
|
||||||
# Setup of field triggers
|
# 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):
|
def setup_triggers(self, env):
|
||||||
""" Add the necessary triggers to invalidate/recompute `self`. """
|
""" Add the necessary triggers to invalidate/recompute `self`. """
|
||||||
|
@ -570,12 +576,12 @@ class Field(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#_logger.debug("Add trigger on %s to recompute %s", field, self)
|
#_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
|
# add trigger on inverse fields, too
|
||||||
for invf in field.inverse_fields:
|
for invf in field.inverse_fields:
|
||||||
#_logger.debug("Add trigger on %s to recompute %s", invf, self)
|
#_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
|
# recursively traverse the dependency
|
||||||
if tail:
|
if tail:
|
||||||
|
@ -1684,8 +1690,8 @@ class One2many(_RelationalMulti):
|
||||||
# (res_model/res_id pattern). Only inverse the field if this is
|
# (res_model/res_id pattern). Only inverse the field if this is
|
||||||
# a `Many2one` field.
|
# a `Many2one` field.
|
||||||
if isinstance(invf, Many2one):
|
if isinstance(invf, Many2one):
|
||||||
self.inverse_fields.append(invf)
|
self.inverse_fields += (invf,)
|
||||||
invf.inverse_fields.append(self)
|
invf.inverse_fields += (self,)
|
||||||
|
|
||||||
_description_relation_field = property(attrgetter('inverse_name'))
|
_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
|
# if inverse field has already been setup, it is present in m2m
|
||||||
invf = m2m.get((self.relation, self.column2, self.column1))
|
invf = m2m.get((self.relation, self.column2, self.column1))
|
||||||
if invf:
|
if invf:
|
||||||
self.inverse_fields.append(invf)
|
self.inverse_fields += (invf,)
|
||||||
invf.inverse_fields.append(self)
|
invf.inverse_fields += (self,)
|
||||||
else:
|
else:
|
||||||
# add self in m2m, so that its inverse field can find it
|
# add self in m2m, so that its inverse field can find it
|
||||||
m2m[(self.relation, self.column1, self.column2)] = self
|
m2m[(self.relation, self.column1, self.column2)] = self
|
||||||
|
|
|
@ -2992,14 +2992,15 @@ class BaseModel(object):
|
||||||
if column:
|
if column:
|
||||||
cls._columns[name] = column
|
cls._columns[name] = column
|
||||||
|
|
||||||
# group fields by compute to determine field.computed_fields
|
# determine field.computed_fields
|
||||||
fields_by_compute = defaultdict(list)
|
computed_fields = defaultdict(list)
|
||||||
for field in cls._fields.itervalues():
|
for field in cls._fields.itervalues():
|
||||||
if field.compute:
|
if field.compute:
|
||||||
field.computed_fields = fields_by_compute[field.compute]
|
computed_fields[field.compute].append(field)
|
||||||
field.computed_fields.append(field)
|
|
||||||
else:
|
for fields in computed_fields.itervalues():
|
||||||
field.computed_fields = []
|
for field in fields:
|
||||||
|
field.computed_fields = fields
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _setup_complete(self):
|
def _setup_complete(self):
|
||||||
|
@ -3017,7 +3018,7 @@ class BaseModel(object):
|
||||||
model = self.env[model_name]
|
model = self.env[model_name]
|
||||||
for field_name in field_names:
|
for field_name in field_names:
|
||||||
field = model._fields[field_name]
|
field = model._fields[field_name]
|
||||||
field._triggers.update(triggers)
|
map(field.add_trigger, triggers)
|
||||||
|
|
||||||
# determine old-api structures about inherited fields
|
# determine old-api structures about inherited fields
|
||||||
cls._inherits_reload()
|
cls._inherits_reload()
|
||||||
|
|
Loading…
Reference in New Issue