Skip to content

Commit 2a83e6b

Browse files
authored
Merge pull request #38775 from slavapestov/requirement-machine-trie
RequirementMachine: Speed up term simplification with a prefix trie
2 parents 6b72c82 + 156fa2c commit 2a83e6b

19 files changed

+1801
-1302
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,9 @@ namespace swift {
464464
/// Enables debugging output from the requirement machine.
465465
bool DebugRequirementMachine = false;
466466

467+
/// Enables statistics output from the requirement machine.
468+
bool AnalyzeRequirementMachine = false;
469+
467470
/// Maximum iteration count for requirement machine confluent completion
468471
/// algorithm.
469472
unsigned RequirementMachineStepLimit = 2000;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ def disable_named_lazy_member_loading : Flag<["-"], "disable-named-lazy-member-l
276276
def debug_requirement_machine : Flag<["-"], "debug-requirement-machine">,
277277
HelpText<"Enables debugging output from the generics implementation">;
278278

279+
def analyze_requirement_machine : Flag<["-"], "analyze-requirement-machine">,
280+
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
281+
HelpText<"Print out requirement machine statistics at the end of the compilation job">;
282+
279283
def requirement_machine_step_limit : Separate<["-"], "requirement-machine-step-limit">,
280284
Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
281285
HelpText<"Set the maximum steps before we give up on confluent completion">;

lib/AST/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ add_swift_host_library(swiftAST STATIC
8080
RequirementMachine/RewriteContext.cpp
8181
RequirementMachine/RewriteSystem.cpp
8282
RequirementMachine/RewriteSystemCompletion.cpp
83+
RequirementMachine/Symbol.cpp
84+
RequirementMachine/Term.cpp
8385
SILLayout.cpp
8486
Stmt.cpp
8587
SubstitutionMap.cpp
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//===--- Histogram.h - Simple histogram for statistics ----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/ADT/ArrayRef.h"
14+
15+
#ifndef SWIFT_HISTOGRAM_H
16+
#define SWIFT_HISTOGRAM_H
17+
18+
#include "llvm/ADT/ArrayRef.h"
19+
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/Support/raw_ostream.h"
21+
#include <algorithm>
22+
#include <vector>
23+
24+
namespace swift {
25+
26+
namespace rewriting {
27+
28+
/// A simple histogram that supports two operations: recording an element, and
29+
/// printing a graphical representation.
30+
///
31+
/// Used by the rewrite system to record various statistics, which are dumped
32+
/// when the -analyze-requirement-machine frontend flag is used.
33+
class Histogram {
34+
unsigned Size;
35+
unsigned Start;
36+
std::vector<unsigned> Buckets;
37+
unsigned OverflowBucket;
38+
39+
const unsigned MaxWidth = 40;
40+
41+
/// This is not really the base-10 logarithm, but rather the number of digits
42+
/// in \p x when printed in base 10.
43+
///
44+
/// So log10(0) == 1, log10(1) == 1, log10(10) == 2, etc.
45+
static unsigned log10(unsigned x) {
46+
if (x == 0)
47+
return 1;
48+
unsigned result = 0;
49+
while (x != 0) {
50+
result += 1;
51+
x /= 10;
52+
}
53+
return result;
54+
}
55+
56+
public:
57+
/// Creates a histogram with \p size buckets, where the numbering begins at
58+
/// \p start.
59+
Histogram(unsigned size, unsigned start = 0)
60+
: Size(size),
61+
Start(start),
62+
Buckets(size, 0),
63+
OverflowBucket(0) {
64+
}
65+
66+
/// Adds an entry to the histogram. Asserts if the \p value is smaller than
67+
/// Start. The value is allowed to be greater than or equal to Start + Size,
68+
/// in which case it's counted in the OverflowBucket.
69+
void add(unsigned value) {
70+
assert(value >= Start);
71+
value -= Start;
72+
if (value >= Size)
73+
++OverflowBucket;
74+
else
75+
++Buckets[value];
76+
}
77+
78+
/// Print a nice-looking graphical representation of the histogram.
79+
void dump(llvm::raw_ostream &out, const StringRef labels[] = {}) {
80+
unsigned sumValues = 0;
81+
unsigned maxValue = 0;
82+
for (unsigned i = 0; i < Size; ++i) {
83+
sumValues += Buckets[i];
84+
maxValue = std::max(maxValue, Buckets[i]);
85+
}
86+
sumValues += OverflowBucket;
87+
maxValue = std::max(maxValue, OverflowBucket);
88+
89+
size_t maxLabelWidth = 3 + log10(Size + Start);
90+
if (labels != nullptr) {
91+
for (unsigned i = 0; i < Size; ++i)
92+
maxLabelWidth = std::max(labels[i].size(), maxLabelWidth);
93+
}
94+
95+
out << std::string(maxLabelWidth, ' ') << " |";
96+
out << std::string(std::min(MaxWidth, maxValue), ' ');
97+
out << maxValue << "\n";
98+
99+
out << std::string(maxLabelWidth, ' ') << " |";
100+
out << std::string(std::min(MaxWidth, maxValue), ' ');
101+
out << "*\n";
102+
103+
out << std::string(maxLabelWidth, '-') << "-+-";
104+
out << std::string(std::min(MaxWidth, maxValue), '-') << "\n";
105+
106+
auto scaledValue = [&](unsigned value) {
107+
if (maxValue > MaxWidth) {
108+
if (value != 0) {
109+
// If the value is non-zero, print at least one '#'.
110+
return std::max(1U, (value * MaxWidth) / maxValue);
111+
}
112+
}
113+
return value;
114+
};
115+
116+
for (unsigned i = 0; i < Size; ++i) {
117+
if (labels) {
118+
out << std::string(maxLabelWidth - labels[i].size(), ' ');
119+
out << labels[i];
120+
} else {
121+
unsigned key = i + Start;
122+
out << std::string(maxLabelWidth - log10(key), ' ');
123+
out << key;
124+
}
125+
out << " | ";
126+
127+
unsigned value = scaledValue(Buckets[i]);
128+
out << std::string(value, '#') << "\n";
129+
}
130+
131+
if (OverflowBucket > 0) {
132+
out << ">= " << (Size + Start) << " | ";
133+
134+
unsigned value = scaledValue(OverflowBucket);
135+
out << std::string(value, '#') << "\n";
136+
}
137+
138+
out << std::string(maxLabelWidth, '-') << "-+-";
139+
out << std::string(std::min(MaxWidth, maxValue), '-') << "\n";
140+
141+
out << std::string(maxLabelWidth, ' ') << " | ";
142+
out << "Total: " << sumValues << "\n";
143+
}
144+
};
145+
146+
} // end namespace rewriting
147+
148+
} // end namespace swift
149+
150+
#endif

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,8 @@ static MutableTerm getRelativeTermForType(CanType typeWitness,
291291
}
292292

293293
// Add the member type names.
294-
std::reverse(symbols.begin(), symbols.end());
295-
for (auto symbol : symbols)
296-
result.add(symbol);
294+
for (auto iter = symbols.rbegin(), end = symbols.rend(); iter != end; ++iter)
295+
result.add(*iter);
297296

298297
return result;
299298
}
@@ -449,8 +448,8 @@ namespace {
449448
///
450449
/// These lower to the following two rules:
451450
///
452-
/// T.[concrete: Foo<τ_0_0, τ_0_1, String> with {X.Y, Z}]
453-
/// T.[concrete: Foo<Int, τ_0_0, τ_0_1> with {A.B, W}]
451+
/// T.[concrete: Foo<τ_0_0, τ_0_1, String> with {X.Y, Z}] => T
452+
/// T.[concrete: Foo<Int, τ_0_0, τ_0_1> with {A.B, W}] => T
454453
///
455454
/// The two concrete type symbols will be added to the property bag of 'T',
456455
/// and we will eventually end up in this method, where we will generate three
@@ -1063,17 +1062,22 @@ RewriteSystem::buildPropertyMap(PropertyMap &map,
10631062
if (rule.isDeleted())
10641063
continue;
10651064

1066-
const auto &lhs = rule.getLHS();
1065+
auto lhs = rule.getLHS();
1066+
auto rhs = rule.getRHS();
10671067

10681068
// Collect all rules of the form T.[p] => T where T is canonical.
10691069
auto property = lhs.back();
10701070
if (!property.isProperty())
10711071
continue;
10721072

1073-
MutableTerm key(lhs.begin(), lhs.end() - 1);
1074-
if (key != rule.getRHS())
1073+
if (lhs.size() - 1 != rhs.size())
10751074
continue;
10761075

1076+
if (!std::equal(rhs.begin(), rhs.end(), lhs.begin()))
1077+
continue;
1078+
1079+
MutableTerm key(rhs);
1080+
10771081
#ifndef NDEBUG
10781082
assert(!simplify(key) &&
10791083
"Right hand side of a property rule should already be reduced");

lib/AST/RequirementMachine/RewriteContext.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
using namespace swift;
2020
using namespace rewriting;
2121

22+
RewriteContext::RewriteContext(ASTContext &ctx)
23+
: Context(ctx),
24+
Stats(ctx.Stats),
25+
SymbolHistogram(Symbol::NumKinds),
26+
TermHistogram(4, /*Start=*/1),
27+
RuleTrieHistogram(16, /*Start=*/1),
28+
RuleTrieRootHistogram(16) {
29+
}
30+
2231
Term RewriteContext::getTermForType(CanType paramType,
2332
const ProtocolDecl *proto) {
2433
return Term::get(getMutableTermForType(paramType, proto), *this);
@@ -271,4 +280,20 @@ Type RewriteContext::getRelativeTypeForTerm(
271280
return getTypeForSymbolRange(
272281
term.begin() + prefix.size(), term.end(), genericParam,
273282
{ }, protos, Context);
274-
}
283+
}
284+
285+
/// We print stats in the destructor, which should get executed at the end of
286+
/// a compilation job.
287+
RewriteContext::~RewriteContext() {
288+
if (Context.LangOpts.AnalyzeRequirementMachine) {
289+
llvm::dbgs() << "--- Requirement Machine Statistics ---\n";
290+
llvm::dbgs() << "\n* Symbol kind:\n";
291+
SymbolHistogram.dump(llvm::dbgs(), Symbol::Kinds);
292+
llvm::dbgs() << "\n* Term length:\n";
293+
TermHistogram.dump(llvm::dbgs());
294+
llvm::dbgs() << "\n* Rule trie fanout:\n";
295+
RuleTrieHistogram.dump(llvm::dbgs());
296+
llvm::dbgs() << "\n* Rule trie root fanout:\n";
297+
RuleTrieRootHistogram.dump(llvm::dbgs());
298+
}
299+
}

lib/AST/RequirementMachine/RewriteContext.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include "swift/Basic/Statistic.h"
1919
#include "llvm/ADT/FoldingSet.h"
2020
#include "llvm/Support/Allocator.h"
21-
#include "RewriteSystem.h"
21+
#include "Histogram.h"
22+
#include "Symbol.h"
23+
#include "Term.h"
2224

2325
namespace swift {
2426

@@ -50,10 +52,16 @@ class RewriteContext final {
5052
ASTContext &Context;
5153

5254
public:
53-
/// Statistical counters.
55+
/// Statistics.
5456
UnifiedStatsReporter *Stats;
5557

56-
RewriteContext(ASTContext &ctx) : Context(ctx), Stats(ctx.Stats) {}
58+
/// Histograms.
59+
Histogram SymbolHistogram;
60+
Histogram TermHistogram;
61+
Histogram RuleTrieHistogram;
62+
Histogram RuleTrieRootHistogram;
63+
64+
explicit RewriteContext(ASTContext &ctx);
5765

5866
Term getTermForType(CanType paramType, const ProtocolDecl *proto);
5967

@@ -73,6 +81,8 @@ class RewriteContext final {
7381
Type getRelativeTypeForTerm(
7482
const MutableTerm &term, const MutableTerm &prefix,
7583
const ProtocolGraph &protos) const;
84+
85+
~RewriteContext();
7686
};
7787

7888
} // end namespace rewriting

0 commit comments

Comments
 (0)