Skip to content

Commit c39ffda

Browse files
authored
Merge pull request #8477 from graydon/rdar-30961871-metrics-mark-2
2 parents ea224b9 + 0245c83 commit c39ffda

File tree

14 files changed

+416
-17
lines changed

14 files changed

+416
-17
lines changed

include/swift/Basic/Statistic.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#ifndef SWIFT_BASIC_STATISTIC_H
1414
#define SWIFT_BASIC_STATISTIC_H
1515

16+
#include "llvm/ADT/SmallString.h"
1617
#include "llvm/ADT/Statistic.h"
18+
#include "swift/Basic/Timer.h"
1719

1820
#define SWIFT_FUNC_STAT \
1921
do { \
@@ -22,4 +24,85 @@
2224
++FStat; \
2325
} while (0)
2426

27+
// Helper class designed to consolidate reporting of LLVM statistics and timers
28+
// across swift compilations that typically invoke many drivers, each running
29+
// many frontends. Additionally collects some cheap "always-on" statistics,
30+
// beyond those that are (compile-time) parametrized by -DLLVM_ENABLE_STATS
31+
// (LLVM's stats are global and involve some amount of locking and mfences).
32+
//
33+
// Assumes it's given a process name and target name (the latter used as
34+
// decoration for its self-timer), and a directory to collect stats into, then:
35+
//
36+
// - On construction:
37+
// - Calls llvm::EnableStatistics(/*PrintOnExit=*/false)
38+
// - Calls swift::enableCompilationTimers()
39+
// - Starts an llvm::NamedRegionTimer for this process
40+
//
41+
// - On destruction:
42+
// - Add any standard always-enabled stats about the process as a whole
43+
// - Opens $dir/stats-$timestamp-$name-$random.json for writing
44+
// - Calls llvm::PrintStatisticsJSON(ostream) and/or its own writer
45+
//
46+
// Generally we make one of these per-process: either early in the life of the
47+
// driver, or early in the life of the frontend.
48+
49+
namespace swift {
50+
51+
class UnifiedStatsReporter {
52+
53+
public:
54+
struct AlwaysOnDriverCounters
55+
{
56+
size_t NumDriverJobsRun;
57+
size_t NumDriverJobsSkipped;
58+
59+
size_t DriverDepCascadingTopLevel;
60+
size_t DriverDepCascadingDynamic;
61+
size_t DriverDepCascadingNominal;
62+
size_t DriverDepCascadingMember;
63+
size_t DriverDepCascadingExternal;
64+
65+
size_t DriverDepTopLevel;
66+
size_t DriverDepDynamic;
67+
size_t DriverDepNominal;
68+
size_t DriverDepMember;
69+
size_t DriverDepExternal;
70+
};
71+
72+
struct AlwaysOnFrontendCounters
73+
{
74+
size_t NumSILGenFunctions;
75+
size_t NumSILGenVtables;
76+
size_t NumSILGenWitnessTables;
77+
size_t NumSILGenDefaultWitnessTables;
78+
size_t NumSILGenGlobalVariables;
79+
80+
size_t NumSILOptFunctions;
81+
size_t NumSILOptVtables;
82+
size_t NumSILOptWitnessTables;
83+
size_t NumSILOptDefaultWitnessTables;
84+
size_t NumSILOptGlobalVariables;
85+
};
86+
87+
private:
88+
SmallString<128> Filename;
89+
std::unique_ptr<llvm::NamedRegionTimer> Timer;
90+
91+
std::unique_ptr<AlwaysOnDriverCounters> DriverCounters;
92+
std::unique_ptr<AlwaysOnFrontendCounters> FrontendCounters;
93+
94+
void publishAlwaysOnStatsToLLVM();
95+
void printAlwaysOnStatsAndTimers(llvm::raw_ostream &OS);
96+
97+
public:
98+
UnifiedStatsReporter(StringRef ProgramName,
99+
StringRef TargetName,
100+
StringRef Directory);
101+
~UnifiedStatsReporter();
102+
103+
AlwaysOnDriverCounters &getDriverCounters();
104+
AlwaysOnFrontendCounters &getFrontendCounters();
105+
};
106+
107+
}
25108
#endif // SWIFT_BASIC_STATISTIC_H

include/swift/Driver/Compilation.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Driver/Util.h"
2222
#include "swift/Basic/ArrayRefView.h"
2323
#include "swift/Basic/LLVM.h"
24+
#include "swift/Basic/Statistic.h"
2425
#include "llvm/ADT/DenseSet.h"
2526
#include "llvm/ADT/StringRef.h"
2627
#include "llvm/Support/Chrono.h"
@@ -134,6 +135,9 @@ class Compilation {
134135
/// execute.
135136
bool ShowDriverTimeCompilation;
136137

138+
/// When non-null, record various high-level counters to this.
139+
std::unique_ptr<UnifiedStatsReporter> Stats;
140+
137141
/// When true, dumps information about why files are being scheduled to be
138142
/// rebuilt.
139143
bool ShowIncrementalBuildDecisions = false;
@@ -156,7 +160,8 @@ class Compilation {
156160
bool EnableIncrementalBuild = false,
157161
bool SkipTaskExecution = false,
158162
bool SaveTemps = false,
159-
bool ShowDriverTimeCompilation = false);
163+
bool ShowDriverTimeCompilation = false,
164+
std::unique_ptr<UnifiedStatsReporter> Stats = nullptr);
160165
~Compilation();
161166

162167
ArrayRefView<std::unique_ptr<const Job>, const Job *, Compilation::unwrap>

include/swift/Driver/DependencyGraph.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace llvm {
3333

3434
namespace swift {
3535

36+
class UnifiedStatsReporter;
37+
3638
/// The non-templated implementation of DependencyGraph.
3739
///
3840
/// \see DependencyGraph
@@ -65,12 +67,14 @@ class DependencyGraphImpl {
6567
class MarkTracerImpl {
6668
class Entry;
6769
llvm::DenseMap<const void *, SmallVector<Entry, 4>> Table;
70+
UnifiedStatsReporter *Stats;
6871

6972
friend class DependencyGraphImpl;
7073
protected:
71-
MarkTracerImpl();
74+
explicit MarkTracerImpl(UnifiedStatsReporter *Stats);
7275
~MarkTracerImpl();
73-
76+
void countStatsForNodeMarking(const OptionSet<DependencyKind> &Kind,
77+
bool Cascading) const;
7478
void printPath(raw_ostream &out, const void *item,
7579
llvm::function_ref<void(const void *)> printItem) const;
7680
};
@@ -223,7 +227,8 @@ class DependencyGraph : public DependencyGraphImpl {
223227
/// This is intended to be a debugging aid.
224228
class MarkTracer : public MarkTracerImpl {
225229
public:
226-
MarkTracer() = default;
230+
explicit MarkTracer(UnifiedStatsReporter *Stats)
231+
: MarkTracerImpl(Stats) {}
227232

228233
/// Dump the path that led to \p node.
229234
void printPath(raw_ostream &out, T node,

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ class FrontendOptions {
192192
/// \sa swift::SharedTimer
193193
bool DebugTimeCompilation = false;
194194

195+
/// The path to which we should output statistics files.
196+
std::string StatsOutputDir;
197+
195198
/// Indicates whether function body parsing should be delayed
196199
/// until the end of all files.
197200
bool DelayedFunctionBodyParsing = false;

include/swift/Option/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ def save_temps : Flag<["-"], "save-temps">,
191191
def driver_time_compilation : Flag<["-"], "driver-time-compilation">,
192192
Flags<[NoInteractiveOption,DoesNotAffectIncrementalBuild]>,
193193
HelpText<"Prints the total time it took to execute all compilation tasks">;
194+
def stats_output_dir: Separate<["-"], "stats-output-dir">,
195+
Flags<[FrontendOption, HelpHidden]>,
196+
HelpText<"Directory to write unified compilation-statistics files to">;
194197

195198
def emit_dependencies : Flag<["-"], "emit-dependencies">,
196199
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>,

lib/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ add_swift_library(swiftBasic STATIC
8383
Program.cpp
8484
QuotedString.cpp
8585
SourceLoc.cpp
86+
Statistic.cpp
8687
StringExtras.cpp
8788
TaskQueue.cpp
8889
ThreadSafeRefCounted.cpp

lib/Basic/Statistic.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
//===--- Statistic.cpp - Swift unified stats reporting --------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2017 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 "swift/Basic/Statistic.h"
14+
#include "swift/Driver/DependencyGraph.h"
15+
#include "swift/SIL/SILModule.h"
16+
#include "llvm/Support/FileSystem.h"
17+
#include "llvm/Support/Path.h"
18+
#include "llvm/Support/Process.h"
19+
#include "llvm/Support/raw_ostream.h"
20+
#include <chrono>
21+
22+
namespace swift {
23+
using namespace llvm;
24+
using namespace llvm::sys;
25+
26+
static std::string
27+
makeFileName(StringRef ProcessName) {
28+
std::string tmp;
29+
raw_string_ostream stream(tmp);
30+
auto now = std::chrono::system_clock::now();
31+
stream << "stats"
32+
<< "-" << now.time_since_epoch().count()
33+
<< "-" << ProcessName
34+
<< "-" << Process::GetRandomNumber()
35+
<< ".json";
36+
return stream.str();
37+
}
38+
39+
UnifiedStatsReporter::UnifiedStatsReporter(StringRef ProgramName,
40+
StringRef TargetName,
41+
StringRef Directory)
42+
: Filename(Directory),
43+
Timer(make_unique<NamedRegionTimer>(TargetName, "Building Target",
44+
ProgramName, "Running Program"))
45+
{
46+
path::append(Filename, makeFileName(ProgramName));
47+
EnableStatistics(/*PrintOnExit=*/false);
48+
SharedTimer::enableCompilationTimers();
49+
}
50+
51+
UnifiedStatsReporter::AlwaysOnDriverCounters &
52+
UnifiedStatsReporter::getDriverCounters()
53+
{
54+
if (!DriverCounters)
55+
DriverCounters = make_unique<AlwaysOnDriverCounters>();
56+
return *DriverCounters;
57+
}
58+
59+
UnifiedStatsReporter::AlwaysOnFrontendCounters &
60+
UnifiedStatsReporter::getFrontendCounters()
61+
{
62+
if (!FrontendCounters)
63+
FrontendCounters = make_unique<AlwaysOnFrontendCounters>();
64+
return *FrontendCounters;
65+
}
66+
67+
#define PUBLISH_STAT(C,TY,NAME) \
68+
do { \
69+
static Statistic Stat = {TY, #NAME, #NAME, {0}, false}; \
70+
Stat += (C).NAME; \
71+
} while(0)
72+
73+
void
74+
UnifiedStatsReporter::publishAlwaysOnStatsToLLVM() {
75+
if (FrontendCounters) {
76+
auto &C = getFrontendCounters();
77+
PUBLISH_STAT(C, "SILModule", NumSILGenFunctions);
78+
PUBLISH_STAT(C, "SILModule", NumSILGenVtables);
79+
PUBLISH_STAT(C, "SILModule", NumSILGenWitnessTables);
80+
PUBLISH_STAT(C, "SILModule", NumSILGenDefaultWitnessTables);
81+
PUBLISH_STAT(C, "SILModule", NumSILGenGlobalVariables);
82+
83+
PUBLISH_STAT(C, "SILModule", NumSILOptFunctions);
84+
PUBLISH_STAT(C, "SILModule", NumSILOptVtables);
85+
PUBLISH_STAT(C, "SILModule", NumSILOptWitnessTables);
86+
PUBLISH_STAT(C, "SILModule", NumSILOptDefaultWitnessTables);
87+
PUBLISH_STAT(C, "SILModule", NumSILOptGlobalVariables);
88+
}
89+
if (DriverCounters) {
90+
auto &C = getDriverCounters();
91+
PUBLISH_STAT(C, "Driver", NumDriverJobsRun);
92+
PUBLISH_STAT(C, "Driver", NumDriverJobsSkipped);
93+
94+
PUBLISH_STAT(C, "Driver", DriverDepCascadingTopLevel);
95+
PUBLISH_STAT(C, "Driver", DriverDepCascadingDynamic);
96+
PUBLISH_STAT(C, "Driver", DriverDepCascadingNominal);
97+
PUBLISH_STAT(C, "Driver", DriverDepCascadingMember);
98+
PUBLISH_STAT(C, "Driver", DriverDepCascadingExternal);
99+
100+
PUBLISH_STAT(C, "Driver", DriverDepTopLevel);
101+
PUBLISH_STAT(C, "Driver", DriverDepDynamic);
102+
PUBLISH_STAT(C, "Driver", DriverDepNominal);
103+
PUBLISH_STAT(C, "Driver", DriverDepMember);
104+
PUBLISH_STAT(C, "Driver", DriverDepExternal);
105+
}
106+
}
107+
108+
#define PRINT_STAT(OS,DELIM,C,TY,NAME) \
109+
do { \
110+
OS << DELIM << "\t\"" TY "." #NAME "\": " << C.NAME; \
111+
delim = ",\n"; \
112+
} while(0)
113+
114+
void
115+
UnifiedStatsReporter::printAlwaysOnStatsAndTimers(raw_ostream &OS) {
116+
// Adapted from llvm::PrintStatisticsJSON
117+
OS << "{\n";
118+
const char *delim = "";
119+
if (FrontendCounters) {
120+
auto &C = getFrontendCounters();
121+
PRINT_STAT(OS, delim, C, "SILModule", NumSILGenFunctions);
122+
PRINT_STAT(OS, delim, C, "SILModule", NumSILGenVtables);
123+
PRINT_STAT(OS, delim, C, "SILModule", NumSILGenWitnessTables);
124+
PRINT_STAT(OS, delim, C, "SILModule", NumSILGenDefaultWitnessTables);
125+
PRINT_STAT(OS, delim, C, "SILModule", NumSILGenGlobalVariables);
126+
127+
PRINT_STAT(OS, delim, C, "SILModule", NumSILOptFunctions);
128+
PRINT_STAT(OS, delim, C, "SILModule", NumSILOptVtables);
129+
PRINT_STAT(OS, delim, C, "SILModule", NumSILOptWitnessTables);
130+
PRINT_STAT(OS, delim, C, "SILModule", NumSILOptDefaultWitnessTables);
131+
PRINT_STAT(OS, delim, C, "SILModule", NumSILOptGlobalVariables);
132+
}
133+
if (DriverCounters) {
134+
auto &C = getDriverCounters();
135+
PRINT_STAT(OS, delim, C, "Driver", NumDriverJobsRun);
136+
PRINT_STAT(OS, delim, C, "Driver", NumDriverJobsSkipped);
137+
138+
PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingTopLevel);
139+
PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingDynamic);
140+
PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingNominal);
141+
PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingMember);
142+
PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingExternal);
143+
144+
PRINT_STAT(OS, delim, C, "Driver", DriverDepTopLevel);
145+
PRINT_STAT(OS, delim, C, "Driver", DriverDepDynamic);
146+
PRINT_STAT(OS, delim, C, "Driver", DriverDepNominal);
147+
PRINT_STAT(OS, delim, C, "Driver", DriverDepMember);
148+
PRINT_STAT(OS, delim, C, "Driver", DriverDepExternal);
149+
}
150+
// Print timers.
151+
TimerGroup::printAllJSONValues(OS, delim);
152+
OS << "\n}\n";
153+
OS.flush();
154+
}
155+
156+
UnifiedStatsReporter::~UnifiedStatsReporter()
157+
{
158+
// NB: Timer needs to be Optional<> because it needs to be destructed early;
159+
// LLVM will complain about double-stopping a timer if you tear down a
160+
// NamedRegionTimer after printing all timers. The printing routines were
161+
// designed with more of a global-scope, run-at-process-exit in mind, which
162+
// we're repurposing a bit here.
163+
Timer.reset();
164+
165+
std::error_code EC;
166+
raw_fd_ostream ostream(Filename, EC, fs::F_Append | fs::F_Text);
167+
if (EC)
168+
return;
169+
170+
// We change behaviour here depending on whether -DLLVM_ENABLE_STATS and/or
171+
// assertions were on in this build; this is somewhat subtle, but turning on
172+
// all stats for all of LLVM and clang is a bit more expensive and intrusive
173+
// than we want to be in release builds.
174+
//
175+
// - If enabled: we copy all of our "always-on" local stats into LLVM's
176+
// global statistics list, and ask LLVM to manage the printing of them.
177+
//
178+
// - If disabled: we still have our "always-on" local stats to write, and
179+
// LLVM's global _timers_ were still enabled (they're runtime-enabled, not
180+
// compile-time) so we sequence printing our own stats and LLVM's timers
181+
// manually.
182+
183+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
184+
publishAlwaysOnStatsToLLVM();
185+
PrintStatisticsJSON(ostream);
186+
#else
187+
printAlwaysOnStatsAndTimers(ostream);
188+
#endif
189+
}
190+
191+
} // namespace swift

0 commit comments

Comments
 (0)