summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@openerp.com>2015-03-11 10:36:17 +0100
committerRaphael Collet <rco@openerp.com>2015-03-23 14:36:15 +0100
commitffa7f28d34a449d65ef353403cc2d2a12e256d93 (patch)
tree24c5d4c8010b592c50530eafe35e8559e3c17564
parent3df77540870c7b021ab056fda0f5d8b316c503e9 (diff)
[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.
-rw-r--r--openerp/fields.py34
-rw-r--r--openerp/models.py15
2 files changed, 28 insertions, 21 deletions
diff --git a/openerp/fields.py b/openerp/fields.py
index 98e5e3f4a1f..3167819534a 100644
--- a/openerp/fields.py
+++ b/openerp/fields.py
@@ -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
diff --git a/openerp/models.py b/openerp/models.py
index 0846521888d..d9f565cb781 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -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()