Skip to content

Commit 2da1163

Browse files
committed
fixup! fixup! fixup! fixup! [BOLT] Profile quality stats -- CFG discontinuity
1 parent ff23095 commit 2da1163

File tree

4 files changed

+65
-82
lines changed

4 files changed

+65
-82
lines changed

bolt/include/bolt/Passes/ContinuityStats.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
1-
//===- bolt/Passes/ContinuityStats.h - function cfg continuity analysis ---*-
2-
// C++ -*-===//
1+
//===- bolt/Passes/ContinuityStats.h ----------------------------*- C++ -*-===//
32
//
43
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
54
// See https://llvm.org/LICENSE.txt for license information.
65
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
76
//
87
//===----------------------------------------------------------------------===//
98
//
10-
// Conduct function CFG continuity analysis.
9+
// This pass checks how well the BOLT input profile satisfies the following
10+
// "CFG continuity" property of a perfect profile:
11+
//
12+
// Each positive-execution-count block in the function’s CFG
13+
// should be *reachable* from a positive-execution-count function
14+
// entry block through a positive-execution-count path.
15+
//
16+
// More specifically, for each of the hottest 1000 functions, the pass
17+
// calculates the function’s fraction of basic block execution counts
18+
// that is *unreachable*. It then reports the 95th percentile of the
19+
// distribution of the 1000 unreachable fractions in a single BOLT-INFO line.
20+
// The smaller the reported value is, the better the BOLT profile
21+
// satisfies the CFG continuity property.
22+
23+
// The default value of 1000 above can be changed via the hidden BOLT option
24+
// `-num-hottest-functions-for-continuity-check=[N]`.
25+
// If more detailed stats are needed, `-v=1` can be used: the hottest N
26+
// functions will be grouped into 5 equally-sized buckets, from the hottest
27+
// to the coldest; for each bucket, various summary statistics of the
28+
// distribution of the unreachable fractions and the raw unreachable execution
29+
// counts will be reported.
1130
//
1231
//===----------------------------------------------------------------------===//
1332

bolt/lib/Passes/ContinuityStats.cpp

Lines changed: 40 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
//===- bolt/Passes/ContinuityStats.cpp - function cfg continuity analysis ---*-
2-
// C++ -*-===//
1+
//===- bolt/Passes/ContinuityStats.cpp --------------------------*- C++ -*-===//
32
//
43
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
54
// See https://llvm.org/LICENSE.txt for license information.
65
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
76
//
87
//===----------------------------------------------------------------------===//
98
//
10-
// Conduct function CFG continuity analysis.
9+
// This file implements the continuity stats calculation pass.
1110
//
1211
//===----------------------------------------------------------------------===//
1312

@@ -27,29 +26,11 @@ using namespace bolt;
2726

2827
namespace opts {
2928
extern cl::opt<unsigned> Verbosity;
30-
cl::opt<unsigned>
31-
NumTopFunctions("num-top-functions",
32-
cl::desc("number of hottest functions to print aggregated "
33-
"CFG discontinuity stats of."),
34-
cl::init(1000), cl::ZeroOrMore, cl::Hidden,
35-
cl::cat(BoltOptCategory));
36-
cl::opt<bool>
37-
PrintBucketedStats("print-bucketed-stats",
38-
cl::desc("print CFG discontinuity stats for the top "
39-
"functions divided into buckets "
40-
"based on their execution counts."),
41-
cl::Hidden, cl::cat(BoltCategory));
42-
cl::opt<unsigned>
43-
NumFunctionsPerBucket("num-functions-per-bucket",
44-
cl::desc("maximum number of functions per bucket."),
45-
cl::init(500), cl::ZeroOrMore, cl::Hidden,
46-
cl::cat(BoltOptCategory));
47-
cl::opt<unsigned>
48-
MinNumFunctions("min-num-functions",
49-
cl::desc("minimum number of hot functions in the binary to "
50-
"trigger profile CFG continuity check."),
51-
cl::init(5), cl::ZeroOrMore, cl::Hidden,
52-
cl::cat(BoltOptCategory));
29+
cl::opt<unsigned> NumFunctionsForContinuityCheck(
30+
"num-functions-for-continuity-check",
31+
cl::desc("number of hottest functions to print aggregated "
32+
"CFG discontinuity stats of."),
33+
cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory));
5334
} // namespace opts
5435

5536
namespace {
@@ -159,8 +140,7 @@ void printCFGContinuityStats(raw_ostream &OS,
159140
size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC;
160141
double FractionECUnreachable = (double)SumUnreachableBBEC / SumAllBBEC;
161142

162-
if (opts::Verbosity >= 2 && FractionECUnreachable > 0.1 &&
163-
SumUnreachableBBEC > 50) {
143+
if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) {
164144
OS << "Non-trivial CFG discontinuity observed in function "
165145
<< Function->getPrintName() << "\n";
166146
LLVM_DEBUG(Function->dump());
@@ -171,15 +151,17 @@ void printCFGContinuityStats(raw_ostream &OS,
171151
FractionECUnreachables.push_back(FractionECUnreachable);
172152
}
173153

154+
if (FractionECUnreachables.empty())
155+
return;
156+
157+
size_t NumConsideredFunctions =
158+
std::distance(Functions.begin(), Functions.end());
174159
if (!Verbose) {
175-
if (FractionECUnreachables.empty()) {
176-
OS << "no functions have more than 1 basic block and hence no CFG "
177-
"discontinuity.\n";
178-
return;
179-
}
180160
std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end());
181161
int Rank = int(FractionECUnreachables.size() * 0.95);
182-
OS << format("the TOP 5%% function CFG discontinuity is %.2lf%%",
162+
OS << format("BOLT-INFO: among the hottest %zu functions ",
163+
NumConsideredFunctions)
164+
<< format("the top 5%% function CFG discontinuity is %.2lf%%",
183165
FractionECUnreachables[Rank] * 100)
184166
<< "\n";
185167
return;
@@ -189,53 +171,41 @@ void printCFGContinuityStats(raw_ostream &OS,
189171
"least 2 basic blocks\n",
190172
SumECUnreachables.size(),
191173
100.0 * (double)SumECUnreachables.size() /
192-
(std::distance(Functions.begin(), Functions.end())));
174+
NumConsideredFunctions);
193175

194-
if (!NumUnreachables.empty()) {
195-
OS << "- Distribution of NUM(unreachable POS BBs) among all focal "
196-
"functions\n";
197-
printDistribution(OS, NumUnreachables);
198-
}
199-
if (!SumECUnreachables.empty()) {
200-
OS << "- Distribution of SUM(unreachable POS BBs) among all focal "
201-
"functions\n";
202-
printDistribution(OS, SumECUnreachables);
203-
}
204-
if (!FractionECUnreachables.empty()) {
205-
OS << "- Distribution of [(SUM(unreachable POS BBs) / SUM(all "
206-
"POS BBs))] among all focal functions\n";
207-
printDistribution(OS, FractionECUnreachables, /*Fraction=*/true);
208-
}
176+
OS << "- Distribution of NUM(unreachable POS BBs) among all focal "
177+
"functions\n";
178+
printDistribution(OS, NumUnreachables);
179+
180+
OS << "- Distribution of SUM(unreachable POS BBs) among all focal "
181+
"functions\n";
182+
printDistribution(OS, SumECUnreachables);
183+
184+
OS << "- Distribution of [(SUM(unreachable POS BBs) / SUM(all "
185+
"POS BBs))] among all focal functions\n";
186+
printDistribution(OS, FractionECUnreachables, /*Fraction=*/true);
209187
}
210188

211189
void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
212-
size_t NumFunctionsPerBucket, size_t NumTopFunctions) {
190+
size_t NumTopFunctions) {
213191
// Sort the list of functions by execution counts (reverse).
214192
llvm::sort(ValidFunctions,
215193
[&](const BinaryFunction *A, const BinaryFunction *B) {
216194
return A->getKnownExecutionCount() > B->getKnownExecutionCount();
217195
});
218196

219197
size_t RealNumTopFunctions = std::min(NumTopFunctions, ValidFunctions.size());
220-
if (RealNumTopFunctions <= opts::MinNumFunctions)
221-
return;
222-
BC.outs() << format("BOLT-INFO: among the hottest %zu functions ",
223-
RealNumTopFunctions);
198+
224199
iterator_range<function_iterator> Functions(
225200
ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions);
226201
printCFGContinuityStats(BC.outs(), Functions, /*Verbose=*/false);
227202

228203
// Print more detailed bucketed stats if requested.
229-
if (opts::PrintBucketedStats) {
230-
size_t PerBucketSize =
231-
std::min(NumFunctionsPerBucket, ValidFunctions.size());
232-
if (PerBucketSize == 0)
233-
return;
234-
size_t NumBuckets = RealNumTopFunctions / PerBucketSize +
235-
(RealNumTopFunctions % PerBucketSize != 0);
204+
if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) {
205+
size_t PerBucketSize = RealNumTopFunctions / 5;
236206
BC.outs() << format(
237-
"Detailed stats for %zu buckets, each with at most %zu functions:\n",
238-
NumBuckets, PerBucketSize);
207+
"Detailed stats for 5 buckets, each with %zu functions:\n",
208+
PerBucketSize);
239209
BC.outs() << "For each considered function, identify positive "
240210
"execution-count basic blocks\n"
241211
<< "(abbr. POS BBs) that are *unreachable* from the function "
@@ -244,10 +214,9 @@ void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
244214

245215
// For each bucket, print the CFG continuity stats of the functions in the
246216
// bucket.
247-
for (size_t BucketIndex = 0; BucketIndex < NumBuckets; ++BucketIndex) {
217+
for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) {
248218
const size_t StartIndex = BucketIndex * PerBucketSize;
249-
size_t EndIndex = std::min(StartIndex + PerBucketSize, NumTopFunctions);
250-
EndIndex = std::min(EndIndex, ValidFunctions.size());
219+
size_t EndIndex = StartIndex + PerBucketSize;
251220
iterator_range<function_iterator> Functions(
252221
ValidFunctions.begin() + StartIndex,
253222
ValidFunctions.begin() + EndIndex);
@@ -282,7 +251,9 @@ Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) {
282251
if (PrintContinuityStats::shouldOptimize(*Function))
283252
ValidFunctions.push_back(Function);
284253
}
285-
printAll(BC, ValidFunctions, opts::NumFunctionsPerBucket,
286-
opts::NumTopFunctions);
254+
if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0)
255+
return Error::success();
256+
257+
printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck);
287258
return Error::success();
288259
}

bolt/lib/Rewrite/BinaryPassManager.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,6 @@ static cl::opt<bool>
155155
cl::desc("print profile quality/bias analysis"),
156156
cl::cat(BoltCategory));
157157

158-
static cl::opt<bool> PrintContinuityStats(
159-
"print-continuity-stats",
160-
cl::desc("print profile function CFG continuity stats"),
161-
cl::cat(BoltCategory));
162-
163158
static cl::opt<bool>
164159
PrintRegReAssign("print-regreassign",
165160
cl::desc("print functions after regreassign pass"),
@@ -379,8 +374,7 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
379374
if (opts::PrintProfileStats)
380375
Manager.registerPass(std::make_unique<PrintProfileStats>(NeverPrint));
381376

382-
if (opts::PrintContinuityStats)
383-
Manager.registerPass(std::make_unique<PrintContinuityStats>(NeverPrint));
377+
Manager.registerPass(std::make_unique<PrintContinuityStats>(NeverPrint));
384378

385379
Manager.registerPass(std::make_unique<ValidateInternalCalls>(NeverPrint));
386380

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
## Check profile discontinuity reporting
22
RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe
3-
RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt \
4-
RUN: --print-continuity-stats --min-num-functions=1 | FileCheck %s
5-
CHECK: among the hottest 5 functions the TOP 5% function CFG discontinuity is 100.00%
3+
RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s
4+
CHECK: among the hottest 5 functions the top 5% function CFG discontinuity is 100.00%

0 commit comments

Comments
 (0)