Memoize a few hot functions during module file generation (#37739)

This commit is contained in:
Massimiliano Culpo 2023-06-01 22:36:42 +02:00
parent 9924c92c40
commit 9fb25b7404
5 changed files with 16 additions and 13 deletions

View File

@ -170,11 +170,9 @@ def merge_config_rules(configuration, spec):
Returns: Returns:
dict: actions to be taken on the spec passed as an argument dict: actions to be taken on the spec passed as an argument
""" """
# Construct a dictionary with the actions we need to perform on the spec passed as a parameter
spec_configuration = {}
# The keyword 'all' is always evaluated first, all the others are # The keyword 'all' is always evaluated first, all the others are
# evaluated in order of appearance in the module file # evaluated in order of appearance in the module file
spec_configuration.update(copy.deepcopy(configuration.get("all", {}))) spec_configuration = copy.deepcopy(configuration.get("all", {}))
for constraint, action in configuration.items(): for constraint, action in configuration.items():
if spec.satisfies(constraint): if spec.satisfies(constraint):
if hasattr(constraint, "override") and constraint.override: if hasattr(constraint, "override") and constraint.override:

View File

@ -134,6 +134,7 @@ def filter_hierarchy_specs(self):
return configuration(self.name).get("filter_hierarchy_specs", {}) return configuration(self.name).get("filter_hierarchy_specs", {})
@property @property
@lang.memoized
def hierarchy_tokens(self): def hierarchy_tokens(self):
"""Returns the list of tokens that are part of the modulefile """Returns the list of tokens that are part of the modulefile
hierarchy. 'compiler' is always present. hierarchy. 'compiler' is always present.
@ -158,6 +159,7 @@ def hierarchy_tokens(self):
return tokens return tokens
@property @property
@lang.memoized
def requires(self): def requires(self):
"""Returns a dictionary mapping all the requirements of this spec """Returns a dictionary mapping all the requirements of this spec
to the actual provider. 'compiler' is always present among the to the actual provider. 'compiler' is always present among the
@ -224,6 +226,7 @@ def available(self):
return available return available
@property @property
@lang.memoized
def missing(self): def missing(self):
"""Returns the list of tokens that are not available.""" """Returns the list of tokens that are not available."""
return [x for x in self.hierarchy_tokens if x not in self.available] return [x for x in self.hierarchy_tokens if x not in self.available]
@ -317,6 +320,7 @@ def available_path_parts(self):
return parts return parts
@property @property
@lang.memoized
def unlocked_paths(self): def unlocked_paths(self):
"""Returns a dictionary mapping conditions to a list of unlocked """Returns a dictionary mapping conditions to a list of unlocked
paths. paths.
@ -428,6 +432,7 @@ def missing(self):
return self.conf.missing return self.conf.missing
@tengine.context_property @tengine.context_property
@lang.memoized
def unlocked_paths(self): def unlocked_paths(self):
"""Returns the list of paths that are unlocked unconditionally.""" """Returns the list of paths that are unlocked unconditionally."""
layout = make_layout(self.spec, self.conf.name, self.conf.explicit) layout = make_layout(self.spec, self.conf.name, self.conf.explicit)

View File

@ -4,7 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import itertools import itertools
import textwrap import textwrap
from typing import List from typing import List, Optional, Tuple
import llnl.util.lang import llnl.util.lang
@ -66,17 +66,17 @@ def to_dict(self):
return dict(d) return dict(d)
def make_environment(dirs=None): @llnl.util.lang.memoized
"""Returns an configured environment for template rendering.""" def make_environment(dirs: Optional[Tuple[str, ...]] = None):
"""Returns a configured environment for template rendering."""
# Import at this scope to avoid slowing Spack startup down
import jinja2
if dirs is None: if dirs is None:
# Default directories where to search for templates # Default directories where to search for templates
builtins = spack.config.get("config:template_dirs", ["$spack/share/spack/templates"]) builtins = spack.config.get("config:template_dirs", ["$spack/share/spack/templates"])
extensions = spack.extensions.get_template_dirs() extensions = spack.extensions.get_template_dirs()
dirs = [canonicalize_path(d) for d in itertools.chain(builtins, extensions)] dirs = tuple(canonicalize_path(d) for d in itertools.chain(builtins, extensions))
# avoid importing this at the top level as it's used infrequently and
# slows down startup a bit.
import jinja2
# Loader for the templates # Loader for the templates
loader = jinja2.FileSystemLoader(dirs) loader = jinja2.FileSystemLoader(dirs)

View File

@ -62,7 +62,7 @@ def source_file(tmpdir, is_relocatable):
src = tmpdir.join("relocatable.c") src = tmpdir.join("relocatable.c")
shutil.copy(template_src, str(src)) shutil.copy(template_src, str(src))
else: else:
template_dirs = [os.path.join(spack.paths.test_path, "data", "templates")] template_dirs = (os.path.join(spack.paths.test_path, "data", "templates"),)
env = spack.tengine.make_environment(template_dirs) env = spack.tengine.make_environment(template_dirs)
template = env.get_template("non_relocatable.c") template = env.get_template("non_relocatable.c")
text = template.render({"prefix": spack.store.layout.root}) text = template.render({"prefix": spack.store.layout.root})

View File

@ -71,7 +71,7 @@ def test_template_retrieval(self):
"""Tests the template retrieval mechanism hooked into config files""" """Tests the template retrieval mechanism hooked into config files"""
# Check the directories are correct # Check the directories are correct
template_dirs = spack.config.get("config:template_dirs") template_dirs = spack.config.get("config:template_dirs")
template_dirs = [canonicalize_path(x) for x in template_dirs] template_dirs = tuple([canonicalize_path(x) for x in template_dirs])
assert len(template_dirs) == 3 assert len(template_dirs) == 3
env = tengine.make_environment(template_dirs) env = tengine.make_environment(template_dirs)