Skip to content

Commit c9c4e10

Browse files
committed
SILOptimizer: add an option to profile and measure the runtime of optimization passes.
When enabling the option `-sil-opt-profile-repeat=<n>`, the optimizer runs passes n times and reports the total runtime at the end of the pass pipeline. This is useful to profile a specific optimization pass with `sil-opt`. For example, to profile the stack promotion pass: ``` sil-opt -stack-promotion -sil-opt-profile-repeat=10000 -o /dev/null test.sil ```
1 parent 57fdba2 commit c9c4e10

File tree

3 files changed

+95
-21
lines changed

3 files changed

+95
-21
lines changed

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ADT/DenseMap.h"
2121
#include "llvm/ADT/SmallVector.h"
2222
#include "llvm/Support/Casting.h"
23+
#include "llvm/Support/Chrono.h"
2324
#include "llvm/Support/ErrorHandling.h"
2425
#include <vector>
2526

@@ -203,6 +204,8 @@ class SILPassManager {
203204
/// pass manager is destroyed.
204205
DeserializationNotificationHandler *deserializationNotificationHandler;
205206

207+
std::chrono::nanoseconds totalPassRuntime = std::chrono::nanoseconds(0);
208+
206209
/// C'tor. It creates and registers all analysis passes, which are defined
207210
/// in Analysis.def. This is private as it should only be used by
208211
/// ExecuteSILPipelineRequest.

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#include "llvm/Support/Debug.h"
3838
#include "llvm/Support/GraphWriter.h"
3939
#include "llvm/Support/ManagedStatic.h"
40-
#include "llvm/Support/Chrono.h"
4140

4241
using namespace swift;
4342

@@ -61,6 +60,10 @@ llvm::cl::opt<std::string> SILNumOptPassesToRun(
6160
"sil-opt-pass-count", llvm::cl::init(""),
6261
llvm::cl::desc("Stop optimizing after <N> passes or <N>.<M> passes/sub-passes"));
6362

63+
llvm::cl::opt<unsigned> SILOptProfileRepeat(
64+
"sil-opt-profile-repeat", llvm::cl::init(1),
65+
llvm::cl::desc("repeat passes N times and report the run time"));
66+
6467
llvm::cl::opt<std::string> SILBreakOnFun(
6568
"sil-break-on-function", llvm::cl::init(""),
6669
llvm::cl::desc(
@@ -518,7 +521,6 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) {
518521
F->dump(getOptions().EmitVerboseSIL);
519522
}
520523

521-
llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now();
522524
if (breakBeforeRunning(F->getName(), SFT))
523525
LLVM_BUILTIN_DEBUGTRAP;
524526
if (SILForceVerifyAll ||
@@ -531,31 +533,64 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) {
531533
assert(changeNotifications == SILAnalysis::InvalidationKind::Nothing
532534
&& "change notifications not cleared");
533535

534-
swiftPassInvocation.startFunctionPassRun(SFT);
536+
llvm::sys::TimePoint<> startTime = std::chrono::system_clock::now();
537+
std::chrono::nanoseconds duration(0);
538+
539+
enum {
540+
// In future we might want to make snapshots with positive number (e.g.
541+
// corresponding to pass indices). Therefore use -1 here to avoid collisions.
542+
SnapshotID = -1
543+
};
544+
545+
unsigned numRepeats = SILOptProfileRepeat;
546+
if (numRepeats > 1) {
547+
// Need to create a snapshot to restore the original state for consecutive runs.
548+
F->createSnapshot(SnapshotID);
549+
}
550+
for (unsigned runIdx = 0; runIdx < numRepeats; runIdx++) {
551+
swiftPassInvocation.startFunctionPassRun(SFT);
552+
553+
// Run it!
554+
SFT->run();
555+
556+
if (changeNotifications != SILAnalysis::InvalidationKind::Nothing) {
557+
// Pause time measurement while invalidating analysis and restoring the snapshot.
558+
duration += (std::chrono::system_clock::now() - startTime);
535559

536-
// Run it!
537-
SFT->run();
560+
if (runIdx < numRepeats - 1) {
561+
invalidateAnalysis(F, SILAnalysis::InvalidationKind::Everything);
562+
F->restoreFromSnapshot(SnapshotID);
563+
} else {
564+
invalidateAnalysis(F, changeNotifications);
565+
}
566+
changeNotifications = SILAnalysis::InvalidationKind::Nothing;
567+
568+
// Continue time measurement (including flushing deleted instructions).
569+
startTime = std::chrono::system_clock::now();
570+
}
571+
Mod->flushDeletedInsts();
572+
swiftPassInvocation.finishedFunctionPassRun();
573+
}
538574

539-
if (changeNotifications != SILAnalysis::InvalidationKind::Nothing) {
540-
invalidateAnalysis(F, changeNotifications);
541-
changeNotifications = SILAnalysis::InvalidationKind::Nothing;
575+
duration += (std::chrono::system_clock::now() - startTime);
576+
totalPassRuntime += duration;
577+
if (SILPrintPassTime) {
578+
double milliSecs = (double)duration.count() / 1000000.;
579+
llvm::dbgs() << llvm::format("%9.3f", milliSecs) << " ms: " << SFT->getTag()
580+
<< " @" << F->getName() << "\n";
542581
}
543-
swiftPassInvocation.finishedFunctionPassRun();
582+
583+
if (numRepeats > 1)
584+
F->deleteSnapshot(SnapshotID);
585+
586+
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
544587

545588
if (SILForceVerifyAll ||
546589
SILForceVerifyAroundPass.end() !=
547590
std::find_if(SILForceVerifyAroundPass.begin(),
548591
SILForceVerifyAroundPass.end(), MatchFun)) {
549592
verifyAnalyses(F);
550593
}
551-
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
552-
Mod->flushDeletedInsts();
553-
554-
auto Delta = (std::chrono::system_clock::now() - StartTime).count();
555-
if (SILPrintPassTime) {
556-
llvm::dbgs() << Delta << " (" << SFT->getID() << "," << F->getName()
557-
<< ")\n";
558-
}
559594

560595
// If this pass invalidated anything, print and verify.
561596
if (doPrintAfter(SFT, F, CurrentPassHasInvalidated)) {
@@ -564,7 +599,7 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) {
564599
}
565600

566601
updateSILModuleStatsAfterTransform(F->getModule(), SFT, *this, NumPassesRun,
567-
Delta);
602+
duration.count());
568603

569604
// Remember if this pass didn't change anything.
570605
if (!CurrentPassHasInvalidated)
@@ -697,9 +732,12 @@ void SILPassManager::runModulePass(unsigned TransIdx) {
697732
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
698733
Mod->flushDeletedInsts();
699734

700-
auto Delta = (std::chrono::system_clock::now() - StartTime).count();
735+
std::chrono::nanoseconds duration = std::chrono::system_clock::now() - StartTime;
736+
totalPassRuntime += duration;
737+
701738
if (SILPrintPassTime) {
702-
llvm::dbgs() << Delta << " (" << SMT->getID() << ",Module)\n";
739+
double milliSecs = (double)duration.count() / 1000000.;
740+
llvm::dbgs() << llvm::format("%9.3f", milliSecs) << " ms: " << SMT->getTag() << "\n";
703741
}
704742

705743
// If this pass invalidated anything, print and verify.
@@ -708,7 +746,7 @@ void SILPassManager::runModulePass(unsigned TransIdx) {
708746
printModule(Mod, Options.EmitVerboseSIL);
709747
}
710748

711-
updateSILModuleStatsAfterTransform(*Mod, SMT, *this, NumPassesRun, Delta);
749+
updateSILModuleStatsAfterTransform(*Mod, SMT, *this, NumPassesRun, duration.count());
712750

713751
if (Options.VerifyAll &&
714752
(CurrentPassHasInvalidated || !SILVerifyWithoutInvalidation)) {
@@ -799,6 +837,12 @@ void SILPassManager::execute() {
799837

800838
/// D'tor.
801839
SILPassManager::~SILPassManager() {
840+
841+
if (SILOptProfileRepeat > 1) {
842+
double milliSecs = (double)totalPassRuntime.count() / 1000000.;
843+
llvm::dbgs() << llvm::format("%9.3f", milliSecs) << " ms: total runtime of all passes\n";
844+
}
845+
802846
// Before we do anything further, verify the module and our analyses. These
803847
// are natural points with which to verify.
804848
//

test/SILOptimizer/pass_profiling.sil

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-sil-opt -stack-promotion -sil-opt-profile-repeat=10 %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
// CHECK: {{.*}} ms: total runtime of all passes
6+
7+
sil_stage canonical
8+
9+
import Builtin
10+
import Swift
11+
import SwiftShims
12+
13+
class XX {
14+
@_hasStorage var x: Int32
15+
16+
init()
17+
}
18+
19+
sil @simple_promote : $@convention(thin) () -> Int32 {
20+
bb0:
21+
%o1 = alloc_ref $XX
22+
%l1 = ref_element_addr %o1 : $XX, #XX.x
23+
%l2 = load %l1 : $*Int32
24+
strong_release %o1 : $XX
25+
return %l2 : $Int32
26+
}
27+

0 commit comments

Comments
 (0)