concretizer: rework spack solve output to handle reuse better

This commit is contained in:
Todd Gamblin 2021-10-25 01:21:19 -07:00
parent c537785f6f
commit ace4586bf8
3 changed files with 137 additions and 75 deletions

View File

@ -122,13 +122,21 @@ def solve(parser, args):
tty.msg("Best of %d considered solutions." % result.nmodels)
tty.msg("Optimization Criteria:")
maxlen = max(len(s) for s in result.criteria)
maxlen = max(len(s[2]) for s in result.criteria)
color.cprint(
"@*{ Priority Criterion %sValue}" % ((maxlen - 10) * " ")
"@*{ Priority Criterion %sInstalled ToBuild}" % ((maxlen - 10) * " ")
)
for i, (name, val) in enumerate(zip(result.criteria, opt)):
fmt = " @K{%%-8d} %%-%ds%%5d" % maxlen
color.cprint(fmt % (i + 1, name, val))
fmt = " @K{%%-8d} %%-%ds%%9s %%7s" % maxlen
for i, (idx, build_idx, name) in enumerate(result.criteria, 1):
color.cprint(
fmt % (
i,
name,
"-" if build_idx is None else opt[idx],
opt[idx] if build_idx is None else opt[build_idx],
)
)
print()
for spec in result.specs:

View File

@ -2,7 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
from __future__ import division, print_function
import collections
import copy
@ -92,6 +92,67 @@ def getter(node):
'DeclaredVersion', ['version', 'idx', 'origin']
)
# Below numbers are used to map names of criteria to the order
# they appear in the solution. See concretize.lp
#: Priority offset for "build" criteria (regular criterio shifted to
#: higher priority for specs we have to build)
build_priority_offset = 200
#: Priority offset of "fixed" criteria (those w/o build criteria)
fixed_priority_offset = 100
def build_criteria_names(costs, tuples):
"""Construct an ordered mapping from criteria names to indices in the cost list."""
# pull optimization criteria names out of the solution
priorities_names = []
num_fixed = 0
for pred, args in tuples:
if pred != "opt_criterion":
continue
priority, name = args[:2]
priority = int(priority)
# add the priority of this opt criterion and its name
priorities_names.append((priority, name))
# if the priority is less than fixed_priority_offset, then it
# has an associated build priority -- the same criterion but for
# nodes that we have to build.
if priority < fixed_priority_offset:
build_priority = priority + build_priority_offset
priorities_names.append((build_priority, name))
else:
num_fixed += 1
# sort the criteria by priority
priorities_names = sorted(priorities_names, reverse=True)
assert len(priorities_names) == len(costs), "Wrong number of optimization criteria!"
# split list into three parts: build criteria, fixed criteria, non-build criteria
num_criteria = len(priorities_names)
num_build = (num_criteria - num_fixed) // 2
build = priorities_names[:num_build]
fixed = priorities_names[num_build:num_build + num_fixed]
installed = priorities_names[num_build + num_fixed:]
# mapping from priority to index in cost list
indices = dict((p, i) for i, (p, n) in enumerate(priorities_names))
# make a list that has each name with its build and non-build priority
criteria = [
(p - fixed_priority_offset + num_build, None, name) for p, name in fixed
]
for (i, name), (b, _) in zip(installed, build):
criteria.append((indices[i], indices[b], name))
return criteria
def issequence(obj):
if isinstance(obj, string_types):
@ -531,13 +592,7 @@ def stringify(x):
# add best spec to the results
result.answers.append((list(min_cost), 0, answers))
# pull optimization criteria names out of the solution
criteria = [
(int(args[0]), args[1]) for name, args in tuples
if name == "opt_criterion"
]
result.criteria = [t[1] for t in sorted(criteria, reverse=True)]
result.criteria = build_criteria_names(min_cost, tuples)
# record the number of models the solver considered
result.nmodels = len(models)
@ -1635,6 +1690,9 @@ def setup(self, driver, specs, tests=False, reuse=False):
class SpecBuilder(object):
"""Class with actions to rebuild a spec from ASP results."""
#: Attributes that don't need actions
ignored_attributes = ["opt_criterion"]
def __init__(self, specs):
self._result = None
self._command_line_specs = specs
@ -1797,6 +1855,9 @@ def build_specs(self, function_tuples):
self._specs = {}
for name, args in function_tuples:
if name in SpecBuilder.ignored_attributes:
continue
action = getattr(self, name, None)
# print out unknown actions so we can display them for debugging

View File

@ -793,7 +793,12 @@ build(Package) :- not hash(Package, _), node(Package).
% defaults and preferences. This is implemented by bumping the priority of optimization
% criteria for built specs -- so that they take precedence over the otherwise
% topmost-priority criterion to reuse what is installed.
build_priority(Package, 100) :- build(Package), node(Package).
%
% The priority ranges are:
% 200+ Shifted priorities for build nodes; correspond to priorities 0 - 99.
% 100 - 199 Unshifted priorities. Currently only includes minimizing #builds.
% 0 - 99 Priorities for non-built nodes.
build_priority(Package, 200) :- build(Package), node(Package).
build_priority(Package, 0) :- not build(Package), node(Package).
#defined installed_hash/2.
@ -807,18 +812,17 @@ build_priority(Package, 0) :- not build(Package), node(Package).
% is displayed (clingo doesn't display sums over empty sets by default)
% Try hard to reuse installed packages (i.e., minimize the number built)
opt_criterion(17, "number of packages to build (vs. reuse)").
#minimize { 0@17: #true }.
#minimize { 1@17,Package : build(Package), optimize_for_reuse() }.
opt_criterion(100, "number of packages to build (vs. reuse)").
#minimize { 0@100: #true }.
#minimize { 1@100,Package : build(Package), optimize_for_reuse() }.
#defined optimize_for_reuse/0.
% Minimize the number of deprecated versions being used
opt_criterion(116, "(build) deprecated versions used").
opt_criterion(16, "deprecated versions used").
#minimize{ 0@116: #true }.
#minimize{ 0@16: #true }.
opt_criterion(14, "deprecated versions used").
#minimize{ 0@214: #true }.
#minimize{ 0@14: #true }.
#minimize{
1@16+Priority,Package
1@14+Priority,Package
: deprecated(Package, _),
build_priority(Package, Priority)
}.
@ -827,33 +831,30 @@ opt_criterion(16, "deprecated versions used").
% 1. Version weight
% 2. Number of variants with a non default value, if not set
% for the root(Package)
opt_criterion(115, "(build) version weight").
opt_criterion(15, "version weight").
#minimize{ 0@115: #true }.
#minimize{ 0@15: #true }.
opt_criterion(13, "version weight").
#minimize{ 0@213: #true }.
#minimize{ 0@13: #true }.
#minimize {
Weight@15+Priority
Weight@13+Priority
: root(Package),version_weight(Package, Weight),
build_priority(Package, Priority)
}.
opt_criterion(114, "(build) number of non-default variants (roots)").
opt_criterion(14, "number of non-default variants (roots)").
#minimize{ 0@114: #true }.
#minimize{ 0@14: #true }.
opt_criterion(12, "number of non-default variants (roots)").
#minimize{ 0@212: #true }.
#minimize{ 0@12: #true }.
#minimize {
Weight@14+Priority,Package,Variant,Value
Weight@12+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
root(Package),
build_priority(Package, Priority)
}.
opt_criterion(112, "(build) preferred providers for roots").
opt_criterion(12, "preferred providers for roots").
#minimize{ 0@112 : #true }.
#minimize{ 0@12: #true }.
opt_criterion(11, "preferred providers for roots").
#minimize{ 0@211 : #true }.
#minimize{ 0@11: #true }.
#minimize{
Weight@12+Priority,Provider,Virtual
Weight@11+Priority,Provider,Virtual
: provider_weight(Provider, Virtual, Weight),
root(Provider),
build_priority(Provider, Priority)
@ -862,12 +863,11 @@ opt_criterion(12, "preferred providers for roots").
% If the value is a multivalued variant there could be multiple
% values set as default. Since a default value has a weight of 0 we
% need to maximize their number below to ensure they're all set
opt_criterion(111, "(build) number of values in multi-valued variants (root)").
opt_criterion(11, "number of values in multi-valued variants (root)").
#minimize{ 0@111 : #true }.
#minimize{ 0@11 : #true }.
opt_criterion(10, "number of values in multi-valued variants (root)").
#minimize{ 0@210 : #true }.
#minimize{ 0@10 : #true }.
#maximize {
1@11+Priority,Package,Variant,Value
1@10+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
not variant_single_value(Package, Variant),
root(Package),
@ -875,11 +875,11 @@ opt_criterion(11, "number of values in multi-valued variants (root)").
}.
% Try to use default variants or variants that have been set
opt_criterion(110, "(build) number of non-default variants (non-roots)").
opt_criterion(10, "number of non-default variants (non-roots)").
#minimize{ 0@10: #true }.
opt_criterion(9, "number of non-default variants (non-roots)").
#minimize{ 0@209: #true }.
#minimize{ 0@9: #true }.
#minimize {
Weight@10+Priority,Package,Variant,Value
Weight@9+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
not root(Package),
build_priority(Package, Priority)
@ -887,62 +887,57 @@ opt_criterion(10, "number of non-default variants (non-roots)").
% Minimize the weights of the providers, i.e. use as much as
% possible the most preferred providers
opt_criterion(109, "(build) preferred providers (non-roots)").
opt_criterion(9, "preferred providers (non-roots)").
#minimize{ 0@109: #true }.
#minimize{ 0@9: #true }.
opt_criterion(8, "preferred providers (non-roots)").
#minimize{ 0@208: #true }.
#minimize{ 0@8: #true }.
#minimize{
Weight@9+Priority,Provider,Virtual
Weight@8+Priority,Provider,Virtual
: provider_weight(Provider, Virtual, Weight), not root(Provider),
build_priority(Provider, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
opt_criterion(108, "(build) compiler mismatches").
opt_criterion(8, "compiler mismatches").
#minimize{ 0@108: #true }.
#minimize{ 0@8: #true }.
opt_criterion(7, "compiler mismatches").
#minimize{ 0@207: #true }.
#minimize{ 0@7: #true }.
#minimize{
1@8+Priority,Package,Dependency
1@7+Priority,Package,Dependency
: compiler_mismatch(Package, Dependency),
build_priority(Package, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
opt_criterion(107, "(build) OS mismatches").
opt_criterion(7, "OS mismatches").
#minimize{ 0@107: #true }.
#minimize{ 0@7: #true }.
opt_criterion(6, "OS mismatches").
#minimize{ 0@206: #true }.
#minimize{ 0@6: #true }.
#minimize{
1@7+Priority,Package,Dependency
1@6+Priority,Package,Dependency
: node_os_mismatch(Package, Dependency),
build_priority(Package, Priority)
}.
opt_criterion(106, "(build) non-preferred OSes").
#minimize{ 0@106: #true }.
#minimize{ 0@6: #true }.
opt_criterion(5, "non-preferred OS's").
#minimize{ 0@205: #true }.
#minimize{ 0@5: #true }.
#minimize{
Weight@6+Priority,Package
Weight@5+Priority,Package
: node_os_weight(Package, Weight),
build_priority(Package, Priority)
}.
% Choose more recent versions for nodes
opt_criterion(105, "(build) version badness").
opt_criterion(5, "version badness").
#minimize{ 0@105: #true }.
#minimize{ 0@5: #true }.
#minimize{ 0@204: #true }.
#minimize{ 0@4: #true }.
#minimize{
Weight@5+Priority,Package
Weight@4+Priority,Package
: version_weight(Package, Weight),
build_priority(Package, Priority)
}.
% Try to use preferred compilers
opt_criterion(103, "(build) non-preferred compilers").
opt_criterion(3, "non-preferred compilers").
#minimize{ 0@103: #true }.
#minimize{ 0@203: #true }.
#minimize{ 0@3: #true }.
#minimize{
Weight@3+Priority,Package
@ -952,9 +947,8 @@ opt_criterion(3, "non-preferred compilers").
% Minimize the number of mismatches for targets in the DAG, try
% to select the preferred target.
opt_criterion(102, "(build) target mismatches").
opt_criterion(2, "target mismatches").
#minimize{ 0@102: #true }.
#minimize{ 0@202: #true }.
#minimize{ 0@2: #true }.
#minimize{
1@2+Priority,Package,Dependency
@ -962,9 +956,8 @@ opt_criterion(2, "target mismatches").
build_priority(Package, Priority)
}.
opt_criterion(101, "(build) non-preferred targets").
opt_criterion(1, "non-preferred targets").
#minimize{ 0@101: #true }.
#minimize{ 0@201: #true }.
#minimize{ 0@1: #true }.
#minimize{
Weight@1+Priority,Package