Skip to content

Commit c8415ae

Browse files
committedNov 20, 2024
Sema: Record the trail step count in solver statistics
Also introduce two new frontend flags: The -solver-scope-threshold flag sets the maximum number of scopes, which was previously hardcoded to 1 million. The -solver-trail-threshold flag sets the maximum number of trail steps, which defaults to 64 million.
1 parent a7b1e78 commit c8415ae

10 files changed

+108
-54
lines changed
 

‎include/swift/Basic/LangOptions.h

+10-6
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,16 @@ namespace swift {
847847
/// than this many seconds.
848848
unsigned ExpressionTimeoutThreshold = 600;
849849

850+
/// The upper bound, in bytes, of temporary data that can be
851+
/// allocated by the constraint solver.
852+
unsigned SolverMemoryThreshold = 512 * 1024 * 1024;
853+
854+
/// The maximum number of scopes we explore before giving up.
855+
unsigned SolverScopeThreshold = 1024 * 1024;
856+
857+
/// The maximum number of trail steps we take before giving up.
858+
unsigned SolverTrailThreshold = 64 * 1024 * 1024;
859+
850860
/// If non-zero, abort the switch statement exhaustiveness checker if
851861
/// the Space::minus function is called more than this many times.
852862
///
@@ -896,12 +906,6 @@ namespace swift {
896906
/// is for testing purposes.
897907
std::vector<std::string> DebugForbidTypecheckPrefixes;
898908

899-
/// The upper bound, in bytes, of temporary data that can be
900-
/// allocated by the constraint solver.
901-
unsigned SolverMemoryThreshold = 512 * 1024 * 1024;
902-
903-
unsigned SolverBindingThreshold = 1024 * 1024;
904-
905909
/// The upper bound to number of sub-expressions unsolved
906910
/// before termination of the shrink phrase of the constraint solver.
907911
unsigned SolverShrinkUnsolvedThreshold = 10;

‎include/swift/Basic/Statistics.def

+7-2
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,14 @@ FRONTEND_STATISTIC(Sema, NumCrossImportsChecked)
184184
/// Number of pairs of modules we've actually found cross-imports for.
185185
FRONTEND_STATISTIC(Sema, NumCrossImportsFound)
186186

187+
/// Number of steps recorded in the trail while solving expression type
188+
/// constraints. A rough proxy for "how much work the expression
189+
/// type checker did".
190+
FRONTEND_STATISTIC(Sema, NumTrailSteps)
191+
187192
/// Number of constraint-solving scopes created in the typechecker, while
188-
/// solving expression type constraints. A rough proxy for "how much work the
189-
/// expression typechecker did".
193+
/// solving expression type constraints. Another rough proxy for "how much
194+
/// work the expression type checker did".
190195
FRONTEND_STATISTIC(Sema, NumConstraintScopes)
191196

192197
/// Number of constraint-solving scopes that were created but which

‎include/swift/Option/FrontendOptions.td

+4
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,10 @@ def enable_volatile_modules : Flag<["-"], "enable-volatile-modules">,
827827

828828
def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">;
829829

830+
def solver_scope_threshold_EQ : Joined<["-"], "solver-scope-threshold=">;
831+
832+
def solver_trail_threshold_EQ : Joined<["-"], "solver-trail-threshold=">;
833+
830834
def solver_disable_shrink :
831835
Flag<["-"], "solver-disable-shrink">,
832836
HelpText<"Disable the shrink phase of expression type checking">;

‎include/swift/Sema/ConstraintSolverStats.def

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CS_STATISTIC(NumConjunctionTerms, "# of conjunction terms explored")
2626
CS_STATISTIC(NumSimplifiedConstraints, "# of constraints simplified")
2727
CS_STATISTIC(NumUnsimplifiedConstraints, "# of constraints not simplified")
2828
CS_STATISTIC(NumSimplifyIterations, "# of simplification iterations")
29-
CS_STATISTIC(NumStatesExplored, "# of solution states explored")
29+
CS_STATISTIC(NumSolverScopes, "# of solution states explored")
30+
CS_STATISTIC(NumTrailSteps, "# of changes recorded in the trail")
3031
CS_STATISTIC(NumComponentsSplit, "# of connected components split")
3132
#undef CS_STATISTIC

‎include/swift/Sema/ConstraintSystem.h

+29-27
Original file line numberDiff line numberDiff line change
@@ -2170,13 +2170,20 @@ class ConstraintSystem {
21702170
/// Counter for type variables introduced.
21712171
unsigned TypeCounter = 0;
21722172

2173+
/// The number of changes recorded in the trail so far during the
2174+
/// solution of this constraint system.
2175+
///
2176+
/// This is a rough proxy for how much work the solver did.
2177+
unsigned NumTrailSteps = 0;
2178+
21732179
/// The number of scopes created so far during the solution
21742180
/// of this constraint system.
21752181
///
2176-
/// This is a measure of complexity of the solution space. A new
2177-
/// scope is created every time we attempt a type variable binding
2178-
/// or explore an option in a disjunction.
2179-
unsigned CountScopes = 0;
2182+
/// A new scope is created every time we attempt a type variable
2183+
/// binding or explore an option in a disjunction.
2184+
///
2185+
/// This is a measure of complexity of the solution space.
2186+
unsigned NumSolverScopes = 0;
21802187

21812188
/// High-water mark of measured memory usage in any sub-scope we
21822189
/// explored.
@@ -2539,24 +2546,12 @@ class ConstraintSystem {
25392546
}
25402547

25412548
/// Update statistics when a scope begins.
2542-
unsigned beginScope() {
2543-
++depth;
2544-
maxDepth = std::max(maxDepth, depth);
2545-
2546-
CS.incrementScopeCounter();
2547-
2548-
return NumStatesExplored++;
2549-
}
2549+
unsigned beginScope();
25502550

25512551
/// Update statistics when a scope ends.
2552-
void endScope(unsigned scopeNumber) {
2553-
ASSERT(depth > 0);
2554-
--depth;
2555-
2556-
unsigned countScopesExplored = NumStatesExplored - scopeNumber;
2557-
if (countScopesExplored == 1)
2558-
CS.incrementLeafScopes();
2559-
}
2552+
void endScope(unsigned scopeNumber,
2553+
uint64_t startTrailSteps,
2554+
uint64_t endTrailSteps);
25602555

25612556
/// Check whether constraint system is allowed to form solutions
25622557
/// even with unbound type variables present.
@@ -2760,11 +2755,11 @@ class ConstraintSystem {
27602755
struct SolverScope {
27612756
ConstraintSystem &cs;
27622757

2763-
/// The length of \c TypeVariables.
2764-
unsigned numTypeVariables;
2758+
/// The length of \c TypeVariables at the start of the scope.
2759+
unsigned startTypeVariables;
27652760

2766-
/// The length of \c Trail.
2767-
unsigned numTrailChanges;
2761+
/// The length of \c Trail at the start of the scope.
2762+
uint64_t startTrailSteps;
27682763

27692764
/// The scope number of this scope. Set when the scope is registered.
27702765
unsigned scopeNumber : 31;
@@ -5538,9 +5533,16 @@ class ConstraintSystem {
55385533
return true;
55395534
}
55405535

5541-
// Bail out once we've looked at a really large number of
5542-
// choices.
5543-
if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) {
5536+
auto &opts = getASTContext().TypeCheckerOpts;
5537+
5538+
// Bail out once we've looked at a really large number of choices.
5539+
if (opts.SolverScopeThreshold && NumSolverScopes > opts.SolverScopeThreshold) {
5540+
isAlreadyTooComplex.first = true;
5541+
return true;
5542+
}
5543+
5544+
// Bail out once we've taken a really large number of steps.
5545+
if (opts.SolverTrailThreshold && NumTrailSteps > opts.SolverTrailThreshold) {
55445546
isAlreadyTooComplex.first = true;
55455547
return true;
55465548
}

‎lib/Frontend/CompilerInvocation.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,10 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
17771777
Opts.DebugConstraintSolverAttempt);
17781778
setUnsignedIntegerArgument(OPT_solver_memory_threshold,
17791779
Opts.SolverMemoryThreshold);
1780+
setUnsignedIntegerArgument(OPT_solver_scope_threshold_EQ,
1781+
Opts.SolverScopeThreshold);
1782+
setUnsignedIntegerArgument(OPT_solver_trail_threshold_EQ,
1783+
Opts.SolverTrailThreshold);
17801784
setUnsignedIntegerArgument(OPT_solver_shrink_unsolved_threshold,
17811785
Opts.SolverShrinkUnsolvedThreshold);
17821786

‎lib/Sema/CSSolver.cpp

+37-9
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ ConstraintSystem::SolverState::~SolverState() {
682682
// Update the "largest" statistics if this system is larger than the
683683
// previous one.
684684
// FIXME: This is not at all thread-safe.
685-
if (NumStatesExplored > LargestNumStatesExplored.getValue()) {
685+
if (NumSolverScopes > LargestNumSolverScopes.getValue()) {
686686
LargestSolutionAttemptNumber = SolutionAttempt-1;
687687
++LargestSolutionAttemptNumber;
688688
#define CS_STATISTIC(Name, Description) \
@@ -695,17 +695,17 @@ ConstraintSystem::SolverState::~SolverState() {
695695

696696
ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
697697
: cs(cs),
698-
numTypeVariables(cs.TypeVariables.size()),
699-
numTrailChanges(cs.solverState->Trail.size()),
698+
startTypeVariables(cs.TypeVariables.size()),
699+
startTrailSteps(cs.solverState->Trail.size()),
700700
scopeNumber(cs.solverState->beginScope()),
701701
moved(0) {
702702
ASSERT(!cs.failedConstraint && "Unexpected failed constraint!");
703703
}
704704

705705
ConstraintSystem::SolverScope::SolverScope(SolverScope &&other)
706706
: cs(other.cs),
707-
numTypeVariables(other.numTypeVariables),
708-
numTrailChanges(other.numTrailChanges),
707+
startTypeVariables(other.startTypeVariables),
708+
startTrailSteps(other.startTrailSteps),
709709
scopeNumber(other.scopeNumber),
710710
moved(0) {
711711
other.moved = 1;
@@ -720,7 +720,7 @@ ConstraintSystem::SolverScope::~SolverScope() {
720720
return;
721721

722722
// Roll back introduced type variables.
723-
truncate(cs.TypeVariables, numTypeVariables);
723+
truncate(cs.TypeVariables, startTypeVariables);
724724

725725
// Move any remaining active constraints into the inactive list.
726726
if (!cs.ActiveConstraints.empty()) {
@@ -731,16 +731,43 @@ ConstraintSystem::SolverScope::~SolverScope() {
731731
cs.ActiveConstraints);
732732
}
733733

734+
uint64_t endTrailSteps = cs.solverState->Trail.size();
735+
734736
// Roll back changes to the constraint system.
735-
cs.solverState->Trail.undo(numTrailChanges);
737+
cs.solverState->Trail.undo(startTrailSteps);
736738

737739
// Update statistics.
738-
cs.solverState->endScope(scopeNumber);
740+
cs.solverState->endScope(scopeNumber,
741+
startTrailSteps,
742+
endTrailSteps);
739743

740744
// Clear out other "failed" state.
741745
cs.failedConstraint = nullptr;
742746
}
743747

748+
unsigned ConstraintSystem::SolverState::beginScope() {
749+
++depth;
750+
maxDepth = std::max(maxDepth, depth);
751+
752+
CS.incrementScopeCounter();
753+
754+
return NumSolverScopes++;
755+
}
756+
757+
/// Update statistics when a scope ends.
758+
void ConstraintSystem::SolverState::endScope(unsigned scopeNumber,
759+
uint64_t startTrailSteps,
760+
uint64_t endTrailSteps) {
761+
ASSERT(depth > 0);
762+
--depth;
763+
764+
NumTrailSteps += (endTrailSteps - startTrailSteps);
765+
766+
unsigned countSolverScopes = NumSolverScopes - scopeNumber;
767+
if (countSolverScopes == 1)
768+
CS.incrementLeafScopes();
769+
}
770+
744771
/// Solve the system of constraints.
745772
///
746773
/// \param allowFreeTypeVariables How to bind free type variables in
@@ -1529,7 +1556,8 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
15291556
if (isDebugMode()) {
15301557
auto &log = llvm::errs();
15311558
log << "\n---Solver statistics---\n";
1532-
log << "Total number of scopes explored: " << solverState->NumStatesExplored << "\n";
1559+
log << "Total number of scopes explored: " << solverState->NumSolverScopes << "\n";
1560+
log << "Total number of trail steps: " << solverState->NumTrailSteps << "\n";
15331561
log << "Maximum depth reached while exploring solutions: " << solverState->maxDepth << "\n";
15341562
if (Timer) {
15351563
auto timeInMillis =

‎lib/Sema/CSStep.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -891,9 +891,10 @@ bool ConjunctionStep::attempt(const ConjunctionElement &element) {
891891
// by dropping all scoring information.
892892
CS.clearScore();
893893

894-
// Reset the scope counter to avoid "too complex" failures
895-
// when closure has a lot of elements in the body.
896-
CS.CountScopes = 0;
894+
// Reset the scope and trail counters to avoid "too complex"
895+
// failures when closure has a lot of elements in the body.
896+
CS.NumSolverScopes = 0;
897+
CS.NumTrailSteps = 0;
897898

898899
// If timer is enabled, let's reset it so that each element
899900
// (expression) gets a fresh time slice to get solved. This

‎lib/Sema/CSStep.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ template <typename P> class BindingStep : public SolverStep {
542542

543543
if (CS.isDebugMode()) {
544544
CS.solverState->Trail.dumpActiveScopeChanges(
545-
llvm::errs(), ActiveChoice->first.numTrailChanges,
545+
llvm::errs(), ActiveChoice->first.startTrailSteps,
546546
CS.solverState->getCurrentIndent());
547547
}
548548

@@ -893,8 +893,12 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
893893
std::optional<Score> BestScore;
894894

895895
/// The number of constraint solver scopes already explored
896-
/// before accepting this conjunction.
897-
llvm::SaveAndRestore<unsigned> OuterScopeCount;
896+
/// before attempting this conjunction.
897+
llvm::SaveAndRestore<unsigned> OuterNumSolverScopes;
898+
899+
/// The number of trail steps already recorded before attempting
900+
/// this conjunction.
901+
llvm::SaveAndRestore<unsigned> OuterNumTrailSteps;
898902

899903
/// The number of milliseconds until outer constraint system
900904
/// is considered "too complex" if timer is enabled.
@@ -929,7 +933,9 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
929933
: BindingStep(cs, {cs, conjunction},
930934
conjunction->isIsolated() ? IsolatedSolutions : solutions),
931935
BestScore(getBestScore()),
932-
OuterScopeCount(cs.CountScopes, 0), Conjunction(conjunction),
936+
OuterNumSolverScopes(cs.NumSolverScopes, 0),
937+
OuterNumTrailSteps(cs.NumTrailSteps, 0),
938+
Conjunction(conjunction),
933939
OuterSolutions(solutions) {
934940
assert(conjunction->getKind() == ConstraintKind::Conjunction);
935941

‎lib/Sema/ConstraintSystem.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ ConstraintSystem::~ConstraintSystem() {
140140
}
141141

142142
void ConstraintSystem::incrementScopeCounter() {
143-
++CountScopes;
144-
// FIXME: (transitional) increment the redundant "always-on" counter.
143+
++NumSolverScopes;
145144
if (auto *Stats = getASTContext().Stats)
146145
++Stats->getFrontendCounters().NumConstraintScopes;
147146
}

0 commit comments

Comments
 (0)