propagation: improve performance

This updates the propagation logic used in `concretize.lp` to avoid rules with `path()`
in the body and instead base propagation around `depends_on()`.
This commit is contained in:
Gregory Becker 2022-11-01 10:01:29 -07:00 committed by Todd Gamblin
parent bc209c470d
commit aa4f478ab8
3 changed files with 68 additions and 61 deletions

View File

@ -1411,7 +1411,7 @@ class Body(object):
clauses.append(f.variant_value(spec.name, vname, value)) clauses.append(f.variant_value(spec.name, vname, value))
if variant.propagate: if variant.propagate:
clauses.append(f.variant_propagate(spec.name, vname)) clauses.append(f.variant_propagate(spec.name, vname, value, spec.name))
# Tell the concretizer that this is a possible value for the # Tell the concretizer that this is a possible value for the
# variant, to account for things like int/str values where we # variant, to account for things like int/str values where we
@ -2074,15 +2074,13 @@ def variant_value(self, pkg, name, value):
# FIXME: is there a way not to special case 'dev_path' everywhere? # FIXME: is there a way not to special case 'dev_path' everywhere?
if name == "dev_path": if name == "dev_path":
self._specs[pkg].variants.setdefault( self._specs[pkg].variants.setdefault(
name, name, spack.variant.SingleValuedVariant(name, value)
spack.variant.SingleValuedVariant(name, value)
) )
return return
if name == "patches": if name == "patches":
self._specs[pkg].variants.setdefault( self._specs[pkg].variants.setdefault(
name, name, spack.variant.MultiValuedVariant(name, value)
spack.variant.MultiValuedVariant(name, value)
) )
return return

View File

@ -31,6 +31,7 @@ opt_criterion(300, "number of input specs not concretized").
attr(Name, A1) :- literal(LiteralID, Name, A1), literal_solved(LiteralID). attr(Name, A1) :- literal(LiteralID, Name, A1), literal_solved(LiteralID).
attr(Name, A1, A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID). attr(Name, A1, A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID).
attr(Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID). attr(Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID).
attr(Name, A1, A2, A3, A4) :- literal(LiteralID, Name, A1, A2, A3, A4), literal_solved(LiteralID).
% For these two atoms we only need implications in one direction % For these two atoms we only need implications in one direction
root(Package) :- attr("root", Package). root(Package) :- attr("root", Package).
@ -52,6 +53,7 @@ variant_default_value_from_cli(Package, Variant, Value)
#defined literal/3. #defined literal/3.
#defined literal/4. #defined literal/4.
#defined literal/5. #defined literal/5.
#defined literal/6.
%----------------------------------------------------------------------------- %-----------------------------------------------------------------------------
% Version semantics % Version semantics
@ -175,18 +177,20 @@ node_version_satisfies(Package, Constraint)
% corresponding spec attributes hold. % corresponding spec attributes hold.
condition_holds(ID) :- condition_holds(ID) :-
condition(ID, _); condition(ID, _);
attr(Name, A1) : condition_requirement(ID, Name, A1); attr(Name, A1) : condition_requirement(ID, Name, A1);
attr(Name, A1, A2) : condition_requirement(ID, Name, A1, A2); attr(Name, A1, A2) : condition_requirement(ID, Name, A1, A2);
attr(Name, A1, A2, A3) : condition_requirement(ID, Name, A1, A2, A3). attr(Name, A1, A2, A3) : condition_requirement(ID, Name, A1, A2, A3);
attr(Name, A1, A2, A3, A4) : condition_requirement(ID, Name, A1, A2, A3, A4).
% condition_holds(ID) implies all imposed_constraints, unless do_not_impose(ID) % condition_holds(ID) implies all imposed_constraints, unless do_not_impose(ID)
% is derived. This allows imposed constraints to be canceled in special cases. % is derived. This allows imposed constraints to be canceled in special cases.
impose(ID) :- condition_holds(ID), not do_not_impose(ID). impose(ID) :- condition_holds(ID), not do_not_impose(ID).
% conditions that hold impose constraints on other specs % conditions that hold impose constraints on other specs
attr(Name, A1) :- impose(ID), imposed_constraint(ID, Name, A1). attr(Name, A1) :- impose(ID), imposed_constraint(ID, Name, A1).
attr(Name, A1, A2) :- impose(ID), imposed_constraint(ID, Name, A1, A2). attr(Name, A1, A2) :- impose(ID), imposed_constraint(ID, Name, A1, A2).
attr(Name, A1, A2, A3) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A3). attr(Name, A1, A2, A3) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A3).
attr(Name, A1, A2, A3, A4) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A3, A4).
% we cannot have additional variant values when we are working with concrete specs % we cannot have additional variant values when we are working with concrete specs
:- node(Package), hash(Package, Hash), :- node(Package), hash(Package, Hash),
@ -202,9 +206,11 @@ attr(Name, A1, A2, A3) :- impose(ID), imposed_constraint(ID, Name, A1, A2, A3).
#defined condition_requirement/3. #defined condition_requirement/3.
#defined condition_requirement/4. #defined condition_requirement/4.
#defined condition_requirement/5. #defined condition_requirement/5.
#defined condition_requirement/6.
#defined imposed_constraint/3. #defined imposed_constraint/3.
#defined imposed_constraint/4. #defined imposed_constraint/4.
#defined imposed_constraint/5. #defined imposed_constraint/5.
#defined imposed_constraint/6.
%----------------------------------------------------------------------------- %-----------------------------------------------------------------------------
% Concrete specs % Concrete specs
@ -378,6 +384,7 @@ possible_provider_weight(Dependency, Virtual, 100, "fallback") :- provider(Depen
#defined required_provider_condition/3. #defined required_provider_condition/3.
#defined required_provider_condition/4. #defined required_provider_condition/4.
#defined required_provider_condition/5. #defined required_provider_condition/5.
#defined required_provider_condition/6.
%----------------------------------------------------------------------------- %-----------------------------------------------------------------------------
% Spec Attributes % Spec Attributes
@ -400,7 +407,7 @@ node_target(Package, Target) :- attr("node_target", Package, Target).
node_target_satisfies(Package, Target) :- attr("node_target_satisfies", Package, Target). node_target_satisfies(Package, Target) :- attr("node_target_satisfies", Package, Target).
variant_value(Package, Variant, Value) :- attr("variant_value", Package, Variant, Value). variant_value(Package, Variant, Value) :- attr("variant_value", Package, Variant, Value).
variant_set(Package, Variant, Value) :- attr("variant_set", Package, Variant, Value). variant_set(Package, Variant, Value) :- attr("variant_set", Package, Variant, Value).
variant_propagate(Package, Variant) :- attr("variant_propagate", Package, Variant). variant_propagate(Package, Variant, Value, Source) :- attr("variant_propagate", Package, Variant, Value, Source).
node_flag(Package, FlagType, Flag) :- attr("node_flag", Package, FlagType, Flag). node_flag(Package, FlagType, Flag) :- attr("node_flag", Package, FlagType, Flag).
node_compiler(Package, Compiler) :- attr("node_compiler", Package, Compiler). node_compiler(Package, Compiler) :- attr("node_compiler", Package, Compiler).
depends_on(Package, Dependency, Type) :- attr("depends_on", Package, Dependency, Type). depends_on(Package, Dependency, Type) :- attr("depends_on", Package, Dependency, Type).
@ -422,7 +429,7 @@ attr("node_target", Package, Target) :- node_target(Package, Target).
attr("node_target_satisfies", Package, Target) :- node_target_satisfies(Package, Target). attr("node_target_satisfies", Package, Target) :- node_target_satisfies(Package, Target).
attr("variant_value", Package, Variant, Value) :- variant_value(Package, Variant, Value). attr("variant_value", Package, Variant, Value) :- variant_value(Package, Variant, Value).
attr("variant_set", Package, Variant, Value) :- variant_set(Package, Variant, Value). attr("variant_set", Package, Variant, Value) :- variant_set(Package, Variant, Value).
attr("variant_propagate", Package, Variant) :- variant_propagate(Package, Variant). attr("variant_propagate", Package, Variant, Value, Source) :- variant_propagate(Package, Variant, Value, Source).
attr("node_flag", Package, FlagType, Flag) :- node_flag(Package, FlagType, Flag). attr("node_flag", Package, FlagType, Flag) :- node_flag(Package, FlagType, Flag).
attr("node_compiler", Package, Compiler) :- node_compiler(Package, Compiler). attr("node_compiler", Package, Compiler) :- node_compiler(Package, Compiler).
attr("depends_on", Package, Dependency, Type) :- depends_on(Package, Dependency, Type). attr("depends_on", Package, Dependency, Type) :- depends_on(Package, Dependency, Type).
@ -509,6 +516,7 @@ error(2, "Attempted to use external for '{0}' which does not satisfy any configu
#defined external_spec_condition/3. #defined external_spec_condition/3.
#defined external_spec_condition/4. #defined external_spec_condition/4.
#defined external_spec_condition/5. #defined external_spec_condition/5.
#defined external_spec_condition/6.
%----------------------------------------------------------------------------- %-----------------------------------------------------------------------------
% Config required semantics % Config required semantics
@ -565,23 +573,23 @@ error(2, "Cannot satisfy requirement group for package '{0}'", Package) :-
variant(Package, Variant) :- variant_condition(ID, Package, Variant), variant(Package, Variant) :- variant_condition(ID, Package, Variant),
condition_holds(ID). condition_holds(ID).
% propagate the variant variant_propagate(Package, Variant, Value, Source) :-
variant_value(Descendant, Variant, Value) :- node(Package),
node(Package), path(Package, Descendant), depends_on(Parent, Package),
variant(Package, Variant), variant_propagate(Parent, Variant, Value, Source),
variant(Descendant, Variant), not variant_set(Package, Variant).
variant_value(Package, Variant, Value),
variant_propagate(Package, Variant),
not variant_set(Descendant, Variant),
variant_possible_value(Descendant, Variant, Value).
error(2, "{0} and dependency {1} cannot both propagate variant '{2}'", Package1, Package2, Variant) :- variant_value(Package, Variant, Value) :-
Package1 != Package2, node(Package),
variant_propagate(Package1, Variant), variant(Package, Variant),
variant_propagate(Package2, Variant), variant_propagate(Package, Variant, Value, _),
path(Package1, Descendent), variant_possible_value(Package, Variant, Value).
path(Package2, Descendent),
build(Package). error(2, "{0} and {1} cannot both propagate variant '{2}' to package {3} with values '{4}' and '{5}'", Source1, Source2, Variant, Package, Value1, Value2) :-
variant_propagate(Package, Variant, Value1, Source1),
variant_propagate(Package, Variant, Value2, Source2),
variant(Package, Variant),
Value1 != Value2.
% a variant cannot be set if it is not a variant on the package % a variant cannot be set if it is not a variant on the package
error(2, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package) error(2, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package)
@ -727,7 +735,7 @@ variant_single_value(Package, "dev_path")
% warnings like 'info: atom does not occur in any rule head'. % warnings like 'info: atom does not occur in any rule head'.
#defined variant/2. #defined variant/2.
#defined variant_sticky/2. #defined variant_sticky/2.
#defined variant_propagate/2. #defined variant_propagate/4.
#defined variant_set/3. #defined variant_set/3.
#defined variant_condition/3. #defined variant_condition/3.
#defined variant_single_value/2. #defined variant_single_value/2.
@ -1033,24 +1041,28 @@ compiler_weight(Package, 100)
% propagate flags when compiler match % propagate flags when compiler match
can_inherit_flags(Package, Dependency, FlagType) can_inherit_flags(Package, Dependency, FlagType)
:- path(Package, Dependency), :- depends_on(Package, Dependency),
node_compiler(Package, Compiler), node_compiler(Package, Compiler),
node_compiler(Dependency, Compiler), node_compiler(Dependency, Compiler),
not node_flag_set(Dependency, FlagType, _), not node_flag_set(Dependency, FlagType, _),
compiler(Compiler), flag_type(FlagType). compiler(Compiler), flag_type(FlagType).
node_flag_inherited(Dependency, FlagType, Flag) node_flag_inherited(Dependency, FlagType, Flag)
:- node_flag_set(Package, FlagType, Flag), can_inherit_flags(Package, Dependency, FlagType), :- node_flag_set(Package, FlagType, Flag), can_inherit_flags(Package, Dependency, FlagType),
node_flag_propagate(Package, FlagType). node_flag_propagate(Package, FlagType).
% Insure propagation % Ensure propagation
:- node_flag_inherited(Package, FlagType, Flag), :- node_flag_inherited(Package, FlagType, Flag),
can_inherit_flags(Package, Dependency, FlagType), can_inherit_flags(Package, Dependency, FlagType),
node_flag_propagate(Package, FlagType). node_flag_propagate(Package, FlagType).
error(0, "{0} and dependency {1} cannot both propagate compiler flags '{2}'", Package, Dependency, FlagType) :- error(2, "{0} and {1} cannot both propagate compiler flags '{2}' to {3}", Source1, Source2, Package, FlagType) :-
node(Dependency), depends_on(Source1, Package),
node_flag_propagate(Package, FlagType), depends_on(Source2, Package),
node_flag_propagate(Dependency, FlagType), node_flag_propagate(Source1, FlagType),
path(Package, Dependency). node_flag_propagate(Source2, FlagType),
can_inherit_flags(Source1, Package, FlagType),
can_inherit_flags(Source2, Package, FlagType),
Source1 != Source2.
% remember where flags came from % remember where flags came from
node_flag_source(Package, FlagType, Package) :- node_flag_set(Package, FlagType, _). node_flag_source(Package, FlagType, Package) :- node_flag_set(Package, FlagType, _).
@ -1060,8 +1072,7 @@ node_flag_source(Dependency, FlagType, Q)
% compiler flags from compilers.yaml are put on nodes if compiler matches % compiler flags from compilers.yaml are put on nodes if compiler matches
node_flag(Package, FlagType, Flag) node_flag(Package, FlagType, Flag)
:- not node_flag_set(Package, FlagType, _), :- compiler_version_flag(Compiler, Version, FlagType, Flag),
compiler_version_flag(Compiler, Version, FlagType, Flag),
node_compiler_version(Package, Compiler, Version), node_compiler_version(Package, Compiler, Version),
flag_type(FlagType), flag_type(FlagType),
compiler(Compiler), compiler(Compiler),

View File

@ -156,13 +156,13 @@
identifier_re = r"\w[\w-]*" identifier_re = r"\w[\w-]*"
compiler_color = "@g" #: color for highlighting compilers compiler_color = "@g" #: color for highlighting compilers
version_color = "@c" #: color for highlighting versions version_color = "@c" #: color for highlighting versions
architecture_color ="@m" #: color for highlighting architectures architecture_color = "@m" #: color for highlighting architectures
enabled_variant_color = "@B" #: color for highlighting enabled variants enabled_variant_color = "@B" #: color for highlighting enabled variants
disabled_variant_color = "r" #: color for highlighting disabled varaints disabled_variant_color = "r" #: color for highlighting disabled varaints
dependency_color = "@." #: color for highlighting dependencies dependency_color = "@." #: color for highlighting dependencies
hash_color = "@K" #: color for highlighting package hashes hash_color = "@K" #: color for highlighting package hashes
#: This map determines the coloring of specs when using color output. #: This map determines the coloring of specs when using color output.
#: We make the fields different colors to enhance readability. #: We make the fields different colors to enhance readability.
@ -4984,19 +4984,19 @@ def __init__(self):
( (
r"\@([\w.\-]*\s*)*(\s*\=\s*\w[\w.\-]*)?", r"\@([\w.\-]*\s*)*(\s*\=\s*\w[\w.\-]*)?",
lambda scanner, val: self.token(VER, val), lambda scanner, val: self.token(VER, val),
), ),
(r"\:", lambda scanner, val: self.token(COLON, val)), (r"\:", lambda scanner, val: self.token(COLON, val)),
(r"\,", lambda scanner, val: self.token(COMMA, val)), (r"\,", lambda scanner, val: self.token(COMMA, val)),
(r"\^", lambda scanner, val: self.token(DEP, val)), (r"\^", lambda scanner, val: self.token(DEP, val)),
(r"\+\+", lambda scanner, val: self.token(D_ON, val)), (r"\+\+", lambda scanner, val: self.token(D_ON, val)),
(r"\+", lambda scanner, val: self.token(ON, val)), (r"\+", lambda scanner, val: self.token(ON, val)),
(r"\-\-", lambda scanner, val: self.token(D_OFF, val)), (r"\-\-", lambda scanner, val: self.token(D_OFF, val)),
(r"\-", lambda scanner, val: self.token(OFF, val)), (r"\-", lambda scanner, val: self.token(OFF, val)),
(r"\~\~", lambda scanner, val: self.token(D_OFF, val)), (r"\~\~", lambda scanner, val: self.token(D_OFF, val)),
(r"\~", lambda scanner, val: self.token(OFF, val)), (r"\~", lambda scanner, val: self.token(OFF, val)),
(r"\%", lambda scanner, val: self.token(PCT, val)), (r"\%", lambda scanner, val: self.token(PCT, val)),
(r"\=\=", lambda scanner, val: self.token(D_EQ, val)), (r"\=\=", lambda scanner, val: self.token(D_EQ, val)),
(r"\=", lambda scanner, val: self.token(EQ, val)), (r"\=", lambda scanner, val: self.token(EQ, val)),
# Filenames match before identifiers, so no initial filename # Filenames match before identifiers, so no initial filename
# component is parsed as a spec (e.g., in subdir/spec.yaml/json) # component is parsed as a spec (e.g., in subdir/spec.yaml/json)
(filename_reg, lambda scanner, v: self.token(FILE, v)), (filename_reg, lambda scanner, v: self.token(FILE, v)),
@ -5133,9 +5133,7 @@ def do_parse(self):
# Raise an error if the previous spec is already # Raise an error if the previous spec is already
# concrete (assigned by hash) # concrete (assigned by hash)
if specs and specs[-1]._hash: if specs and specs[-1]._hash:
raise RedundantSpecError(specs[-1], raise RedundantSpecError(specs[-1], "compiler, version, " "or variant")
'compiler, version, '
'or variant')
specs.append(self.spec(None)) specs.append(self.spec(None))
else: else:
self.unexpected_token() self.unexpected_token()