[IMP] models: speedup loading of registry (-20%)

Idea: look up for the model's fields in method `_setup_base()` instead of
method `__init__()`.  This does not make a significant difference when
installing or upgrading modules, but when simply loading a registry, the
(expensive) field lookup is done once per model instead of once per class.
This commit is contained in:
Raphael Collet 2015-02-17 12:07:50 +01:00
parent 0167acbb52
commit 45a37b22fd
2 changed files with 34 additions and 49 deletions

View File

@ -297,6 +297,13 @@ class Field(object):
self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
self._free_attrs = []
# 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 new(self, **kwargs):
""" Return a field of the same type as `self`, with its own parameters. """
return type(self)(**kwargs)
@ -396,16 +403,6 @@ class Field(object):
# Field setup
#
def reset(self):
""" Prepare `self` for a new setup. """
self.setup_done = False
# 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 setup(self, env):
""" Make sure that `self` is set up, except for recomputation triggers. """
if not self.setup_done:

View File

@ -636,8 +636,8 @@ class BaseModel(object):
attrs = {
'_name': name,
'_register': False,
'_columns': {}, # filled by _setup_fields()
'_defaults': {}, # filled by Field._determine_default()
'_columns': None, # recomputed in _setup_fields()
'_defaults': None, # recomputed in _setup_base()
'_inherits': dict(cls._inherits),
'_depends': dict(cls._depends),
'_constraints': list(cls._constraints),
@ -750,9 +750,13 @@ class BaseModel(object):
cls._onchange_methods = defaultdict(list)
for attr, func in getmembers(cls, callable):
if hasattr(func, '_constrains'):
if not all(name in cls._fields for name in func._constrains):
_logger.warning("@constrains%r parameters must be field names", func._constrains)
cls._constraint_methods.append(func)
if hasattr(func, '_onchange'):
for name in func._onchange:
if name not in cls._fields:
_logger.warning("@onchange%r parameters must be field names", func._onchange)
cls._onchange_methods[name].append(func)
def __new__(cls):
@ -800,19 +804,6 @@ class BaseModel(object):
"TransientModels must have log_access turned on, " \
"in order to implement their access rights policy"
# retrieve new-style fields (from above registry class) and duplicate
# them (to avoid clashes with inheritance between different models)
cls._fields = {}
above = cls.__bases__[0]
for attr, field in getmembers(above, Field.__instancecheck__):
cls._add_field(attr, field.new())
# introduce magic fields
cls._add_magic_fields()
# register constraints and onchange methods
cls._init_constraints_onchanges()
# prepare ormcache, which must be shared by all instances of the model
cls._ormcache = {}
@ -2941,26 +2932,28 @@ class BaseModel(object):
if cls._setup_done:
return
# first make sure that parent models determine all their fields
# 1. determine the proper fields of the model; duplicate them on cls to
# avoid clashes with inheritance between different models
for name in getattr(cls, '_fields', {}):
delattr(cls, name)
# retrieve fields from parent classes
cls._fields = {}
cls._defaults = {}
for attr, field in getmembers(cls, Field.__instancecheck__):
cls._add_field(attr, field.new())
# add magic and custom fields
cls._add_magic_fields()
cls._init_manual_fields(self._cr, partial)
# 2. make sure that parent models determine their own fields, then add
# inherited fields to cls
cls._inherits_check()
for parent in cls._inherits:
self.env[parent]._setup_base(partial)
# remove inherited fields from cls._fields
for name, field in cls._fields.items():
if field.inherited:
del cls._fields[name]
# retrieve custom fields
cls._init_manual_fields(self._cr, partial)
# retrieve inherited fields
cls._init_inherited_fields()
# prepare the setup of fields
for field in cls._fields.itervalues():
field.reset()
cls._setup_done = True
@api.model
@ -2968,7 +2961,8 @@ class BaseModel(object):
""" Setup the fields, except for recomputation triggers. """
cls = type(self)
# set up fields, and update their corresponding columns
# set up fields, and determine their corresponding column
cls._columns = {}
for name, field in cls._fields.iteritems():
field.setup(self.env)
if field.store or field.column:
@ -3007,14 +3001,8 @@ class BaseModel(object):
# register stuff about low-level function fields
cls._init_function_fields(cls.pool, self._cr)
# check constraints
for func in cls._constraint_methods:
if not all(name in cls._fields for name in func._constrains):
_logger.warning("@constrains%r parameters must be field names", func._constrains)
for name in cls._onchange_methods:
if name not in cls._fields:
func = cls._onchange_methods[name]
_logger.warning("@onchange%r parameters must be field names", func._onchange)
# register constraints and onchange methods
cls._init_constraints_onchanges()
# check defaults
for name in cls._defaults: