Skip to content

Stats tracer timer work #14695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
123 changes: 80 additions & 43 deletions include/swift/Basic/Statistic.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,35 +56,28 @@ namespace clang {
namespace swift {

class Decl;
class ProtocolConformance;
class Expr;
class SILFunction;
class FrontendStatsTracer;

class UnifiedStatsReporter {

public:
struct AlwaysOnDriverCounters
{
#define DRIVER_STATISTIC(ID) size_t ID;
#define DRIVER_STATISTIC(ID) int64_t ID;
#include "Statistics.def"
#undef DRIVER_STATISTIC
};

struct AlwaysOnFrontendCounters
{
#define FRONTEND_STATISTIC(NAME, ID) size_t ID;
#define FRONTEND_STATISTIC(NAME, ID) int64_t ID;
#include "Statistics.def"
#undef FRONTEND_STATISTIC
};

struct AlwaysOnFrontendRecursiveSharedTimers {
AlwaysOnFrontendRecursiveSharedTimers();
#define FRONTEND_RECURSIVE_SHARED_TIMER(ID) RecursiveSharedTimer ID;
#include "Statistics.def"
#undef FRONTEND_RECURSIVE_SHARED_TIMER

int dummyInstanceVariableToGetConstructorToParse;
};

// To trace an entity, you have to provide a TraceFormatter for it. This is a
// separate type since we do not have retroactive conformances in C++, and it
// is a type that takes void* arguments since we do not have existentials
Expand All @@ -98,38 +91,25 @@ class UnifiedStatsReporter {
virtual ~TraceFormatter();
};

struct FrontendStatsTracer
{
UnifiedStatsReporter *Reporter;
llvm::TimeRecord SavedTime;
StringRef EventName;
const void *Entity;
const TraceFormatter *Formatter;
FrontendStatsTracer(StringRef EventName,
const void *Entity,
const TraceFormatter *Formatter,
UnifiedStatsReporter *Reporter);
FrontendStatsTracer();
FrontendStatsTracer(FrontendStatsTracer&& other);
FrontendStatsTracer& operator=(FrontendStatsTracer&&);
~FrontendStatsTracer();
FrontendStatsTracer(const FrontendStatsTracer&) = delete;
FrontendStatsTracer& operator=(const FrontendStatsTracer&) = delete;
};

struct FrontendStatsEvent
{
uint64_t TimeUSec;
uint64_t LiveUSec;
bool IsEntry;
StringRef EventName;
StringRef CounterName;
size_t CounterDelta;
size_t CounterValue;
int64_t CounterDelta;
int64_t CounterValue;
const void *Entity;
const TraceFormatter *Formatter;
};

// We only write fine-grained trace entries when the user passed
// -trace-stats-events, but we recycle the same FrontendStatsTracers to give
// us some free recursion-save phase timings whenever -trace-stats-dir is
// active at all. Reduces redundant machinery.
class RecursionSafeTimers;

private:
bool currentProcessExitStatusSet;
int currentProcessExitStatus;
Expand All @@ -143,8 +123,7 @@ class UnifiedStatsReporter {
std::unique_ptr<AlwaysOnFrontendCounters> FrontendCounters;
std::unique_ptr<AlwaysOnFrontendCounters> LastTracedFrontendCounters;
std::vector<FrontendStatsEvent> FrontendStatsEvents;
std::unique_ptr<AlwaysOnFrontendRecursiveSharedTimers>
FrontendRecursiveSharedTimers;
std::unique_ptr<RecursionSafeTimers> RecursiveTimers;

void publishAlwaysOnStatsToLLVM();
void printAlwaysOnStatsAndTimers(llvm::raw_ostream &OS);
Expand All @@ -170,18 +149,76 @@ class UnifiedStatsReporter {

AlwaysOnDriverCounters &getDriverCounters();
AlwaysOnFrontendCounters &getFrontendCounters();
AlwaysOnFrontendRecursiveSharedTimers &getFrontendRecursiveSharedTimers();
void noteCurrentProcessExitStatus(int);
// We declare 4 explicit overloads here, but the _definitions_ live in the
// upper-level files (in libswiftAST or libswiftSIL) that provide the types
// being traced. If you want to trace those types, it's assumed you're linking
// with the object files that define the tracer.
FrontendStatsTracer getStatsTracer(StringRef EventName, const Decl *D);
FrontendStatsTracer getStatsTracer(StringRef EventName, const clang::Decl*D);
FrontendStatsTracer getStatsTracer(StringRef EventName, const Expr *E);
FrontendStatsTracer getStatsTracer(StringRef EventName, const SILFunction *F);
void saveAnyFrontendStatsEvents(FrontendStatsTracer const &T, bool IsEntry);
};

// This is a non-nested type just to make it less work to write at call sites.
class FrontendStatsTracer
{
FrontendStatsTracer(UnifiedStatsReporter *Reporter,
StringRef EventName,
const void *Entity,
const UnifiedStatsReporter::TraceFormatter *Formatter);

// In the general case we do not know how to format an entity for tracing.
template<typename T> static
const UnifiedStatsReporter::TraceFormatter *getTraceFormatter() {
return nullptr;
}

public:
UnifiedStatsReporter *Reporter;
llvm::TimeRecord SavedTime;
StringRef EventName;
const void *Entity;
const UnifiedStatsReporter::TraceFormatter *Formatter;
FrontendStatsTracer();
FrontendStatsTracer(FrontendStatsTracer&& other);
FrontendStatsTracer& operator=(FrontendStatsTracer&&);
~FrontendStatsTracer();
FrontendStatsTracer(const FrontendStatsTracer&) = delete;
FrontendStatsTracer& operator=(const FrontendStatsTracer&) = delete;

/// These are the convenience constructors you want to be calling throughout
/// the compiler: they select an appropriate trace formatter for the provided
/// entity type, and produce a tracer that's either active or inert depending
/// on whether the provided \p Reporter is null (nullptr means "tracing is
/// disabled").
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName);
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName,
const Decl *D);
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName,
const ProtocolConformance *P);
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName,
const clang::Decl *D);
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName,
const Expr *E);
FrontendStatsTracer(UnifiedStatsReporter *Reporter, StringRef EventName,
const SILFunction *F);
};

// In particular cases, we do know how to format traced entities: we declare
// explicit specializations of getTraceFormatter() here, matching the overloaded
// constructors of FrontendStatsTracer above, where the _definitions_ live in
// the upper-level files (in libswiftAST or libswiftSIL), and provide tracing
// for those entity types. If you want to trace those types, it's assumed you're
// linking with the object files that define the tracer.

template<> const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const Decl *>();

template<> const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const ProtocolConformance *>();

template<> const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const clang::Decl *>();

template<> const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const Expr *>();

template<> const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const SILFunction *>();

}
#endif // SWIFT_BASIC_STATISTIC_H
13 changes: 0 additions & 13 deletions include/swift/Basic/Statistics.def
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
// - Subsystem is a token to be stringified as a name prefix
// - Id is an identifier suitable for use in C++
//
// FRONTEND_RECURSIVE_SHARED_TIMER(Id)
// - Id is an identifier suitable for use in C++
//
//===----------------------------------------------------------------------===//

/// Driver statistics are collected for driver processes
Expand Down Expand Up @@ -219,13 +216,3 @@ FRONTEND_STATISTIC(IRModule, NumIRInsts)
/// the .o file you find on disk after the frontend exits.
FRONTEND_STATISTIC(LLVM, NumLLVMBytesOutput)
#endif

/// Frontend timers for recursive routines
#ifdef FRONTEND_RECURSIVE_SHARED_TIMER

/// Time spent in NominalTypeDecl::lookupDirect.
FRONTEND_RECURSIVE_SHARED_TIMER(NominalTypeDecl__lookupDirect)

/// Time spent in ClangImporter::Implementation::loadAllMembers.
FRONTEND_RECURSIVE_SHARED_TIMER(ClangImporter__Implementation__loadAllMembers)
#endif
60 changes: 0 additions & 60 deletions include/swift/Basic/Timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,66 +45,6 @@ namespace swift {
CompilationTimersEnabled = State::Enabled;
}
};

/// A SharedTimer for recursive routines.
/// void example() {
/// RecursiveSharedTimer::Guard guard; // MUST BE AT TOP SCOPE of function to
/// work right! if (auto s = getASTContext().Stats) {
/// guard =
/// ctx.Stats->getFrontendRecursiveSharedTimers().NominalTypeDecl__lookupDirect.getGuard();
// }
/// ...
/// }

class RecursiveSharedTimer {
private:
int recursionCount = 0;
const StringRef name;
llvm::Optional<SharedTimer> timer;

void enterRecursiveFunction() {
assert(recursionCount >= 0 && "too many exits");
if (recursionCount++ == 0)
timer.emplace(name);
}
void exitRecursiveFunction() {
assert(recursionCount > 0 && "too many exits");
if (--recursionCount == 0)
timer.reset();
}

public:
RecursiveSharedTimer(StringRef name) : name(name) {}

struct Guard {
RecursiveSharedTimer *recursiveTimerOrNull;

Guard(RecursiveSharedTimer *rst) : recursiveTimerOrNull(rst) {
if (recursiveTimerOrNull)
recursiveTimerOrNull->enterRecursiveFunction();
}
~Guard() {
if (recursiveTimerOrNull)
recursiveTimerOrNull->exitRecursiveFunction();
}

// All this stuff is to do an RAII object that be moved.
Guard() : recursiveTimerOrNull(nullptr) {}
Guard(Guard &&other) {
recursiveTimerOrNull = other.recursiveTimerOrNull;
other.recursiveTimerOrNull = nullptr;
}
Guard &operator=(Guard &&other) {
recursiveTimerOrNull = other.recursiveTimerOrNull;
other.recursiveTimerOrNull = nullptr;
return *this;
}
Guard(const Guard &) = delete;
Guard &operator=(const Guard &) = delete;
};

Guard getGuard() { return Guard(this); }
};
} // end namespace swift

#endif // SWIFT_BASIC_TIMER_H
2 changes: 1 addition & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,7 @@ void ASTContext::loadObjCMethods(

void ASTContext::verifyAllLoadedModules() const {
#ifndef NDEBUG
SharedTimer("verifyAllLoadedModules");
FrontendStatsTracer tracer(Stats, "verify-all-loaded-modules");
for (auto &loader : Impl.ModuleLoaders)
loader->verifyAllModules();

Expand Down
16 changes: 8 additions & 8 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5666,6 +5666,10 @@ struct DeclTraceFormatter : public UnifiedStatsReporter::TraceFormatter {
const Decl *D = static_cast<const Decl *>(Entity);
if (auto const *VD = dyn_cast<const ValueDecl>(D)) {
VD->getFullName().print(OS, false);
} else {
OS << "<"
<< Decl::getDescriptiveKindName(D->getDescriptiveKind())
<< ">";
}
}
void traceLoc(const void *Entity, SourceManager *SM,
Expand All @@ -5679,12 +5683,8 @@ struct DeclTraceFormatter : public UnifiedStatsReporter::TraceFormatter {

static DeclTraceFormatter TF;

UnifiedStatsReporter::FrontendStatsTracer
UnifiedStatsReporter::getStatsTracer(StringRef EventName, const Decl *D) {
if (LastTracedFrontendCounters)
// Return live tracer object.
return FrontendStatsTracer(EventName, D, &TF, this);
else
// Return inert tracer object.
return FrontendStatsTracer();
template<>
const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const Decl *>() {
return &TF;
}
12 changes: 4 additions & 8 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2297,12 +2297,8 @@ struct ExprTraceFormatter : public UnifiedStatsReporter::TraceFormatter {

static ExprTraceFormatter TF;

UnifiedStatsReporter::FrontendStatsTracer
UnifiedStatsReporter::getStatsTracer(StringRef EventName, const Expr *E) {
if (LastTracedFrontendCounters)
// Return live tracer object.
return FrontendStatsTracer(EventName, E, &TF, this);
else
// Return inert tracer object.
return FrontendStatsTracer();
template<>
const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const Expr *>() {
return &TF;
}
4 changes: 1 addition & 3 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1352,12 +1352,10 @@ void NominalTypeDecl::makeMemberVisible(ValueDecl *member) {
TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
DeclName name,
bool ignoreNewExtensions) {
RecursiveSharedTimer::Guard guard;
ASTContext &ctx = getASTContext();
FrontendStatsTracer tracer(ctx.Stats, "lookup-direct", this);
if (auto s = ctx.Stats) {
++s->getFrontendCounters().NominalTypeLookupDirectCount;
guard = s->getFrontendRecursiveSharedTimers()
.NominalTypeDecl__lookupDirect.getGuard();
}

// We only use NamedLazyMemberLoading when a user opts-in and we have
Expand Down
44 changes: 44 additions & 0 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "swift/AST/Substitution.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeWalker.h"
#include "swift/Basic/Statistic.h"
#include "swift/ClangImporter/ClangModule.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Statistic.h"
Expand Down Expand Up @@ -1367,3 +1368,46 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const {
return *this;
return ProtocolConformanceRef(getConcrete()->getCanonicalConformance());
}

// See swift/Basic/Statistic.h for declaration: this enables tracing
// ProtocolConformances, is defined here to avoid too much layering violation /
// circular linkage dependency.

struct ProtocolConformanceTraceFormatter
: public UnifiedStatsReporter::TraceFormatter {
void traceName(const void *Entity, raw_ostream &OS) const {
if (!Entity)
return;
const ProtocolConformance *C =
static_cast<const ProtocolConformance *>(Entity);
OS << "<conformance ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conformances already have a printName method that probably covers this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fixed.

if (auto const *DC = C->getDeclContext()) {
if (auto const *N = DC->getAsNominalTypeOrNominalTypeExtensionContext()) {
N->getFullName().print(OS, false);
}
}
OS << " : ";
if (auto *P = C->getProtocol())
P->getFullName().print(OS, false);
OS << ">";
}
void traceLoc(const void *Entity, SourceManager *SM,
clang::SourceManager *CSM, raw_ostream &OS) const {
if (!Entity)
return;
const ProtocolConformance *C =
static_cast<const ProtocolConformance *>(Entity);
if (auto const *DC = C->getDeclContext()) {
if (auto const *D = DC->getAsDeclOrDeclExtensionContext())
D->getSourceRange().print(OS, *SM, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you want the range here, just the location. (Arguably if you have a NormalProtocolConformance you could get an even better location, which is where the conformance is written. But not all conformances are NormalProtocolConformances.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I know the method is named traceLoc but so far I've been actually tracing all of the ranges. Maybe I should rename it? Or maybe ranges are overkill?

(Will use the normalProtocolConformance decl site when available, in any case)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might be reasonable for some of the other things, but for conformances you'll end up using the entire range of the extension or decl that declares conformance, or maybe just the class that inherits from a conforming decl. That doesn't seem useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok (I think I initially went with ranges because of exprs, but shrug it's pretty much just a hint for the reader anyway).

}
}
};

static ProtocolConformanceTraceFormatter TF;

template<>
const UnifiedStatsReporter::TraceFormatter*
FrontendStatsTracer::getTraceFormatter<const ProtocolConformance *>() {
return &TF;
}
Loading