Skip to content

Commit 0cd27cd

Browse files
committed
[PassTimingInfo] Stop double (or worse) counting passes/analyses
If we nest timers, we end up double counting anything nested. The most egregious is ModuleInlinerWrapperPass/DevirtSCCRepeatedPass showing up as >20% of the total time when they're just wrappers. Analyses also end up getting counted multiple times because they're nested inside wrappers and passes. Ignore ModuleInlinerWrapperPass/DevirtSCCRepeatedPass and put analyses into their own TimerGroup. Reviewed By: asbirlea Differential Revision: https://reviews.llvm.org/D135219
1 parent e68b0d5 commit 0cd27cd

File tree

4 files changed

+93
-60
lines changed

4 files changed

+93
-60
lines changed

llvm/include/llvm/IR/PassTimingInfo.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,19 @@ class TimePassesHandler {
4747
/// to all the instance of a given pass) + sequential invocation counter.
4848
using PassInvocationID = std::pair<StringRef, unsigned>;
4949

50-
/// A group of all pass-timing timers.
51-
TimerGroup TG;
50+
/// Groups of timers for passes and analyses.
51+
TimerGroup PassTG;
52+
TimerGroup AnalysisTG;
5253

5354
using TimerVector = llvm::SmallVector<std::unique_ptr<Timer>, 4>;
5455
/// Map of timers for pass invocations
5556
StringMap<TimerVector> TimingData;
5657

57-
/// Stack of currently active timers.
58-
SmallVector<Timer *, 8> TimerStack;
58+
/// Currently active pass timer.
59+
Timer *ActivePassTimer = nullptr;
60+
/// Stack of currently active analysis timers. Analyses can request other
61+
/// analyses.
62+
SmallVector<Timer *, 8> AnalysisActiveTimerStack;
5963

6064
/// Custom output stream to print timing information into.
6165
/// By default (== nullptr) we emit time report into the stream created by
@@ -89,14 +93,12 @@ class TimePassesHandler {
8993
LLVM_DUMP_METHOD void dump() const;
9094

9195
/// Returns the new timer for each new run of the pass.
92-
Timer &getPassTimer(StringRef PassID);
96+
Timer &getPassTimer(StringRef PassID, bool IsPass);
9397

94-
void startTimer(StringRef PassID);
95-
void stopTimer(StringRef PassID);
96-
97-
// Implementation of pass instrumentation callbacks.
98-
void runBeforePass(StringRef PassID);
99-
void runAfterPass(StringRef PassID);
98+
void startAnalysisTimer(StringRef PassID);
99+
void stopAnalysisTimer(StringRef PassID);
100+
void startPassTimer(StringRef PassID);
101+
void stopPassTimer(StringRef PassID);
100102
};
101103

102104
} // namespace llvm

llvm/lib/IR/PassTimingInfo.cpp

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ class PassTimingInfo {
9494

9595
static ManagedStatic<sys::SmartMutex<true>> TimingInfoMutex;
9696

97-
PassTimingInfo::PassTimingInfo()
98-
: TG("pass", "... Pass execution timing report ...") {}
97+
PassTimingInfo::PassTimingInfo() : TG("pass", "Pass execution timing report") {}
9998

10099
PassTimingInfo::~PassTimingInfo() {
101100
// Deleting the timers accumulates their info into the TG member.
@@ -170,7 +169,8 @@ void reportAndResetTimings(raw_ostream *OutStream) {
170169

171170
/// Returns the timer for the specified pass invocation of \p PassID.
172171
/// Each time it creates a new timer.
173-
Timer &TimePassesHandler::getPassTimer(StringRef PassID) {
172+
Timer &TimePassesHandler::getPassTimer(StringRef PassID, bool IsPass) {
173+
TimerGroup &TG = IsPass ? PassTG : AnalysisTG;
174174
if (!PerRun) {
175175
TimerVector &Timers = TimingData[PassID];
176176
if (Timers.size() == 0)
@@ -193,8 +193,9 @@ Timer &TimePassesHandler::getPassTimer(StringRef PassID) {
193193
}
194194

195195
TimePassesHandler::TimePassesHandler(bool Enabled, bool PerRun)
196-
: TG("pass", "... Pass execution timing report ..."), Enabled(Enabled),
197-
PerRun(PerRun) {}
196+
: PassTG("pass", "Pass execution timing report"),
197+
AnalysisTG("analysis", "Analysis execution timing report"),
198+
Enabled(Enabled), PerRun(PerRun) {}
198199

199200
TimePassesHandler::TimePassesHandler()
200201
: TimePassesHandler(TimePassesIsEnabled, TimePassesPerRun) {}
@@ -206,7 +207,16 @@ void TimePassesHandler::setOutStream(raw_ostream &Out) {
206207
void TimePassesHandler::print() {
207208
if (!Enabled)
208209
return;
209-
TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true);
210+
std::unique_ptr<raw_ostream> MaybeCreated;
211+
raw_ostream *OS = OutStream;
212+
if (OutStream) {
213+
OS = OutStream;
214+
} else {
215+
MaybeCreated = CreateInfoOutputFile();
216+
OS = &*MaybeCreated;
217+
}
218+
PassTG.print(*OS, true);
219+
AnalysisTG.print(*OS, true);
210220
}
211221

212222
LLVM_DUMP_METHOD void TimePassesHandler::dump() const {
@@ -233,61 +243,77 @@ LLVM_DUMP_METHOD void TimePassesHandler::dump() const {
233243
}
234244
}
235245

236-
void TimePassesHandler::startTimer(StringRef PassID) {
237-
Timer &MyTimer = getPassTimer(PassID);
238-
TimerStack.push_back(&MyTimer);
239-
if (!MyTimer.isRunning())
240-
MyTimer.startTimer();
246+
static bool shouldIgnorePass(StringRef PassID) {
247+
return isSpecialPass(PassID,
248+
{"PassManager", "PassAdaptor", "AnalysisManagerProxy",
249+
"ModuleInlinerWrapperPass", "DevirtSCCRepeatedPass"});
241250
}
242251

243-
void TimePassesHandler::stopTimer(StringRef PassID) {
244-
assert(TimerStack.size() > 0 && "empty stack in popTimer");
245-
Timer *MyTimer = TimerStack.pop_back_val();
246-
assert(MyTimer && "timer should be present");
247-
if (MyTimer->isRunning())
248-
MyTimer->stopTimer();
252+
void TimePassesHandler::startPassTimer(StringRef PassID) {
253+
if (shouldIgnorePass(PassID))
254+
return;
255+
assert(!ActivePassTimer && "should only have one pass timer at a time");
256+
Timer &MyTimer = getPassTimer(PassID, /*IsPass*/ true);
257+
ActivePassTimer = &MyTimer;
258+
assert(!MyTimer.isRunning());
259+
MyTimer.startTimer();
249260
}
250261

251-
void TimePassesHandler::runBeforePass(StringRef PassID) {
252-
if (isSpecialPass(PassID,
253-
{"PassManager", "PassAdaptor", "AnalysisManagerProxy"}))
262+
void TimePassesHandler::stopPassTimer(StringRef PassID) {
263+
if (shouldIgnorePass(PassID))
254264
return;
265+
assert(ActivePassTimer);
266+
assert(ActivePassTimer->isRunning());
267+
ActivePassTimer->stopTimer();
268+
ActivePassTimer = nullptr;
269+
}
255270

256-
startTimer(PassID);
271+
void TimePassesHandler::startAnalysisTimer(StringRef PassID) {
272+
// Stop the previous analysis timer to prevent double counting when an
273+
// analysis requests another analysis.
274+
if (!AnalysisActiveTimerStack.empty()) {
275+
assert(AnalysisActiveTimerStack.back()->isRunning());
276+
AnalysisActiveTimerStack.back()->stopTimer();
277+
}
257278

258-
LLVM_DEBUG(dbgs() << "after runBeforePass(" << PassID << ")\n");
259-
LLVM_DEBUG(dump());
279+
Timer &MyTimer = getPassTimer(PassID, /*IsPass*/ false);
280+
AnalysisActiveTimerStack.push_back(&MyTimer);
281+
if (!MyTimer.isRunning())
282+
MyTimer.startTimer();
260283
}
261284

262-
void TimePassesHandler::runAfterPass(StringRef PassID) {
263-
if (isSpecialPass(PassID,
264-
{"PassManager", "PassAdaptor", "AnalysisManagerProxy"}))
265-
return;
266-
267-
stopTimer(PassID);
285+
void TimePassesHandler::stopAnalysisTimer(StringRef PassID) {
286+
assert(!AnalysisActiveTimerStack.empty() && "empty stack in popTimer");
287+
Timer *MyTimer = AnalysisActiveTimerStack.pop_back_val();
288+
assert(MyTimer && "timer should be present");
289+
if (MyTimer->isRunning())
290+
MyTimer->stopTimer();
268291

269-
LLVM_DEBUG(dbgs() << "after runAfterPass(" << PassID << ")\n");
270-
LLVM_DEBUG(dump());
292+
// Restart the previously stopped timer.
293+
if (!AnalysisActiveTimerStack.empty()) {
294+
assert(!AnalysisActiveTimerStack.back()->isRunning());
295+
AnalysisActiveTimerStack.back()->startTimer();
296+
}
271297
}
272298

273299
void TimePassesHandler::registerCallbacks(PassInstrumentationCallbacks &PIC) {
274300
if (!Enabled)
275301
return;
276302

277303
PIC.registerBeforeNonSkippedPassCallback(
278-
[this](StringRef P, Any) { this->runBeforePass(P); });
304+
[this](StringRef P, Any) { this->startPassTimer(P); });
279305
PIC.registerAfterPassCallback(
280306
[this](StringRef P, Any, const PreservedAnalyses &) {
281-
this->runAfterPass(P);
307+
this->stopPassTimer(P);
282308
});
283309
PIC.registerAfterPassInvalidatedCallback(
284310
[this](StringRef P, const PreservedAnalyses &) {
285-
this->runAfterPass(P);
311+
this->stopPassTimer(P);
286312
});
287313
PIC.registerBeforeAnalysisCallback(
288-
[this](StringRef P, Any) { this->runBeforePass(P); });
314+
[this](StringRef P, Any) { this->startAnalysisTimer(P); });
289315
PIC.registerAfterAnalysisCallback(
290-
[this](StringRef P, Any) { this->runAfterPass(P); });
316+
[this](StringRef P, Any) { this->stopAnalysisTimer(P); });
291317
}
292318

293319
} // namespace llvm

llvm/test/Other/time-passes.ll

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,34 @@
2727
; TIME-DOUBLE-LICM-DAG: LICMPass #4
2828
; TIME-DOUBLE-LICM-DAG: LICMPass #5
2929
; TIME-DOUBLE-LICM-DAG: LICMPass #6
30-
; TIME-PER_RUN-DAG: LCSSAPass
31-
; TIME-PER_RUN-DAG: LoopSimplifyPass
32-
; TIME-PER_RUN-DAG: ScalarEvolutionAnalysis
33-
; TIME-PER_RUN-DAG: LoopAnalysis
34-
; TIME-PER_RUN-DAG: VerifierPass
35-
; TIME-PER_RUN-DAG: DominatorTreeAnalysis
36-
; TIME-PER_RUN-DAG: TargetLibraryAnalysis
30+
; TIME-PER-RUN-DAG: LCSSAPass
31+
; TIME-PER-RUN-DAG: LoopSimplifyPass
32+
; TIME-PER-RUN-DAG: VerifierPass
3733
; TIME-PER-PASS-DAG: InstCombinePass
3834
; TIME-PER-PASS-DAG: LICMPass
3935
; TIME-PER-PASS-DAG: LCSSAPass
4036
; TIME-PER-PASS-DAG: LoopSimplifyPass
41-
; TIME-PER-PASS-DAG: ScalarEvolutionAnalysis
42-
; TIME-PER-PASS-DAG: LoopAnalysis
4337
; TIME-PER-PASS-DAG: VerifierPass
44-
; TIME-PER-PASS-DAG: DominatorTreeAnalysis
45-
; TIME-PER-PASS-DAG: TargetLibraryAnalysis
4638
; TIME-PER-PASS-NOT: InstCombinePass #
4739
; TIME-PER-PASS-NOT: LICMPass #
4840
; TIME-PER-PASS-NOT: LCSSAPass #
4941
; TIME-PER-PASS-NOT: LoopSimplifyPass #
42+
; TIME-PER-PASS-NOT: VerifierPass #
43+
; TIME: Total{{$}}
44+
45+
; TIME: Analysis execution timing report
46+
; TIME: Total Execution Time:
47+
; TIME: Name
48+
; TIME-PER-RUN-DAG: ScalarEvolutionAnalysis
49+
; TIME-PER-RUN-DAG: LoopAnalysis
50+
; TIME-PER-RUN-DAG: DominatorTreeAnalysis
51+
; TIME-PER-RUN-DAG: TargetLibraryAnalysis
52+
; TIME-PER-PASS-DAG: ScalarEvolutionAnalysis
53+
; TIME-PER-PASS-DAG: LoopAnalysis
54+
; TIME-PER-PASS-DAG: DominatorTreeAnalysis
55+
; TIME-PER-PASS-DAG: TargetLibraryAnalysis
5056
; TIME-PER-PASS-NOT: ScalarEvolutionAnalysis #
5157
; TIME-PER-PASS-NOT: LoopAnalysis #
52-
; TIME-PER-PASS-NOT: VerifierPass #
5358
; TIME-PER-PASS-NOT: DominatorTreeAnalysis #
5459
; TIME-PER-PASS-NOT: TargetLibraryAnalysis #
5560
; TIME: Total{{$}}

llvm/unittests/IR/TimePassesTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ TEST(TimePassesTest, CustomOut) {
135135

136136
// Pretending that passes are running to trigger the timers.
137137
PI.runBeforePass(Pass1, M);
138+
PI.runAfterPass(Pass1, M, PreservedAnalyses::all());
138139
PI.runBeforePass(Pass2, M);
139140
PI.runAfterPass(Pass2, M, PreservedAnalyses::all());
140-
PI.runAfterPass(Pass1, M, PreservedAnalyses::all());
141141

142142
// Generating report.
143143
TimePasses->print();

0 commit comments

Comments
 (0)