[IMP] core: use cache for mro in setup_models

With a lot of module installed setup_models can be quite slow.
One of the reason for that are the call to resolve_mro.

This operation is fast, but called for a lot of models/fields this
become an important part of get_depends and setup_base.

- This is call once for each models in setup_base
- This is call more than once for each computed_field in get_depends.

The use of a cache shows significative performance improvements.

During the install of a database with all modules, the install times
is reduced by ~3 minutes over ~14

Part-of: odoo/odoo#78898
Co-authored-by: Raphael Collet <rco@odoo.com>
This commit is contained in:
Xavier-Do
2021-10-26 13:22:40 +00:00
committed by Raphael Collet
co-authored by Raphael Collet
parent f40abdce92
commit d4539c188a
2 changed files with 17 additions and 11 deletions
+8 -9
View File
@@ -58,14 +58,13 @@ def resolve_mro(model, name, predicate):
classes are ignored.
"""
result = []
for cls in type(model).mro():
if not is_registry_class(cls):
value = cls.__dict__.get(name, Default)
if value is Default:
continue
if not predicate(value):
break
result.append(value)
for cls in model._model_classes:
value = cls.__dict__.get(name, Default)
if value is Default:
continue
if not predicate(value):
break
result.append(value)
return result
@@ -4100,6 +4099,6 @@ def apply_required(model, field_name):
# pylint: disable=wrong-import-position
from .exceptions import AccessError, MissingError, UserError
from .models import (
check_pg_name, expand_ids, is_definition_class, is_registry_class,
check_pg_name, expand_ids, is_definition_class,
BaseModel, IdType, NewId, PREFETCH_MAX,
)
+9 -2
View File
@@ -2986,6 +2986,12 @@ class BaseModel(metaclass=MetaModel):
if cls._setup_done:
return
# the classes that define this model, i.e., the ones that are not
# registry classes; the purpose of this attribute is to behave as a
# cache of [c for c in cls.mro() if not is_registry_class(c))], which
# is heavily used in function fields.resolve_mro()
cls._model_classes = tuple(c for c in cls.mro() if getattr(c, 'pool', None) is None)
# 1. determine the proper fields of the model: the fields defined on the
# class and magic fields, not the inherited or custom ones
@@ -2997,8 +3003,9 @@ class BaseModel(metaclass=MetaModel):
# collect the definitions of each field (base definition + overrides)
definitions = defaultdict(list)
for klass in reversed(cls.mro()):
if is_definition_class(klass):
for klass in reversed(cls._model_classes):
# this condition is an optimization of is_definition_class(klass)
if isinstance(klass, MetaModel):
for field in klass._field_definitions:
definitions[field.name].append(field)
for name, fields_ in definitions.items():