Times spec building and timing to public concretizer API (#47310)

This PR has two small contributions:
- It adds another phase to the timer for concrectization, "construct_specs", to actually see the time the concretizer spends interpreting the `clingo` output to build the Python object for a concretized spec. 
- It adds the method `Solver.solve_with_stats` to expose the timers that were already in the concretizer to the public solver API. `Solver.solve` just becomes a special case of `Solver.solve_with_stats` that throws away the timing output (which is what it was already doing).  

These changes will make it easier to benchmark concretizer performance and provide a more complete picture of the time spent in the concretizer by including the time spent interpreting clingo output.
This commit is contained in:
John Gouwar 2024-11-04 12:48:18 -05:00 committed by GitHub
parent 8c3068809f
commit 23ac56edfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -889,6 +889,7 @@ def on_model(model):
result.satisfiable = solve_result.satisfiable result.satisfiable = solve_result.satisfiable
if result.satisfiable: if result.satisfiable:
timer.start("construct_specs")
# get the best model # get the best model
builder = SpecBuilder(specs, hash_lookup=setup.reusable_and_possible) builder = SpecBuilder(specs, hash_lookup=setup.reusable_and_possible)
min_cost, best_model = min(models) min_cost, best_model = min(models)
@ -913,7 +914,8 @@ def on_model(model):
# record the possible dependencies in the solve # record the possible dependencies in the solve
result.possible_dependencies = setup.pkgs result.possible_dependencies = setup.pkgs
timer.stop("construct_specs")
timer.stop()
elif cores: elif cores:
result.control = self.control result.control = self.control
result.cores.extend(cores) result.cores.extend(cores)
@ -4191,7 +4193,7 @@ def _check_input_and_extract_concrete_specs(specs):
spack.spec.Spec.ensure_valid_variants(s) spack.spec.Spec.ensure_valid_variants(s)
return reusable return reusable
def solve( def solve_with_stats(
self, self,
specs, specs,
out=None, out=None,
@ -4202,6 +4204,8 @@ def solve(
allow_deprecated=False, allow_deprecated=False,
): ):
""" """
Concretize a set of specs and track the timing and statistics for the solve
Arguments: Arguments:
specs (list): List of ``Spec`` objects to solve for. specs (list): List of ``Spec`` objects to solve for.
out: Optionally write the generate ASP program to a file-like object. out: Optionally write the generate ASP program to a file-like object.
@ -4213,15 +4217,22 @@ def solve(
setup_only (bool): if True, stop after setup and don't solve (default False). setup_only (bool): if True, stop after setup and don't solve (default False).
allow_deprecated (bool): allow deprecated version in the solve allow_deprecated (bool): allow deprecated version in the solve
""" """
# Check upfront that the variants are admissible
specs = [s.lookup_hash() for s in specs] specs = [s.lookup_hash() for s in specs]
reusable_specs = self._check_input_and_extract_concrete_specs(specs) reusable_specs = self._check_input_and_extract_concrete_specs(specs)
reusable_specs.extend(self.selector.reusable_specs(specs)) reusable_specs.extend(self.selector.reusable_specs(specs))
setup = SpackSolverSetup(tests=tests) setup = SpackSolverSetup(tests=tests)
output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only) output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only)
result, _, _ = self.driver.solve( return self.driver.solve(
setup, specs, reuse=reusable_specs, output=output, allow_deprecated=allow_deprecated setup, specs, reuse=reusable_specs, output=output, allow_deprecated=allow_deprecated
) )
def solve(self, specs, **kwargs):
"""
Convenience function for concretizing a set of specs and ignoring timing
and statistics. Uses the same kwargs as solve_with_stats.
"""
# Check upfront that the variants are admissible
result, _, _ = self.solve_with_stats(specs, **kwargs)
return result return result
def solve_in_rounds( def solve_in_rounds(