Skip to content

Commit 042a38f

Browse files
authored
[Support] Optimize DebugCounter (#170305)
Currently, DebugCounters work by creating a unique counter ID during registration, and then using that ID to look up the counter information in the global registry. However, this means that anything working with counters has to always go through the global instance. This includes the fast path that checks whether any counters are enabled. Instead, we can drop the counter IDs, and make the counter variables use CounterInfo themselves. We can then directly check whether the specific counter is active without going through the global registry. This is both faster for the fast-path where all counters are disabled, and also faster for the case where only one counter is active (as the fast-path can now still be used for all the disabled counters). After this change, disabled counters become essentially free at runtime, and we should be able to enable them in non-assert builds as well.
1 parent 9f634c6 commit 042a38f

File tree

3 files changed

+90
-107
lines changed

3 files changed

+90
-107
lines changed

llvm/include/llvm/Support/DebugCounter.h

Lines changed: 61 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444

4545
#include "llvm/ADT/ArrayRef.h"
4646
#include "llvm/ADT/DenseMap.h"
47+
#include "llvm/ADT/MapVector.h"
4748
#include "llvm/ADT/StringRef.h"
48-
#include "llvm/ADT/UniqueVector.h"
4949
#include "llvm/Support/Compiler.h"
5050
#include "llvm/Support/Debug.h"
5151
#include <string>
@@ -63,6 +63,29 @@ class DebugCounter {
6363
bool contains(int64_t Idx) const { return Idx >= Begin && Idx <= End; }
6464
};
6565

66+
/// Struct to store counter info.
67+
class CounterInfo {
68+
friend class DebugCounter;
69+
70+
/// Whether counting should be enabled, either due to -debug-counter or
71+
/// -print-debug-counter.
72+
bool Active = false;
73+
/// Whether chunks for the counter are set (differs from Active in that
74+
/// -print-debug-counter uses Active=true, IsSet=false).
75+
bool IsSet = false;
76+
77+
int64_t Count = 0;
78+
uint64_t CurrChunkIdx = 0;
79+
StringRef Name;
80+
StringRef Desc;
81+
SmallVector<Chunk> Chunks;
82+
83+
public:
84+
CounterInfo(StringRef Name, StringRef Desc) : Name(Name), Desc(Desc) {
85+
DebugCounter::registerCounter(this);
86+
}
87+
};
88+
6689
LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Chunk>);
6790

6891
/// Return true on parsing error and print the error message on the
@@ -75,48 +98,46 @@ class DebugCounter {
7598
// Used by the command line option parser to push a new value it parsed.
7699
LLVM_ABI void push_back(const std::string &);
77100

78-
// Register a counter with the specified name.
101+
// Register a counter with the specified counter information.
79102
//
80103
// FIXME: Currently, counter registration is required to happen before command
81104
// line option parsing. The main reason to register counters is to produce a
82105
// nice list of them on the command line, but i'm not sure this is worth it.
83-
static unsigned registerCounter(StringRef Name, StringRef Desc) {
84-
return instance().addCounter(std::string(Name), std::string(Desc));
106+
static void registerCounter(CounterInfo *Info) {
107+
instance().addCounter(Info);
85108
}
86-
LLVM_ABI static bool shouldExecuteImpl(unsigned CounterName);
109+
LLVM_ABI static bool shouldExecuteImpl(CounterInfo &Counter);
87110

88-
inline static bool shouldExecute(unsigned CounterName) {
89-
if (!isCountingEnabled())
111+
inline static bool shouldExecute(CounterInfo &Counter) {
112+
// Compile to nothing when debugging is off
113+
#ifdef NDEBUG
114+
return true;
115+
#else
116+
if (!Counter.Active)
90117
return true;
91-
return shouldExecuteImpl(CounterName);
118+
return shouldExecuteImpl(Counter);
119+
#endif
92120
}
93121

94122
// Return true if a given counter had values set (either programatically or on
95123
// the command line). This will return true even if those values are
96124
// currently in a state where the counter will always execute.
97-
static bool isCounterSet(unsigned ID) {
98-
return instance().Counters[ID].IsSet;
99-
}
125+
static bool isCounterSet(CounterInfo &Info) { return Info.IsSet; }
100126

101127
struct CounterState {
102128
int64_t Count;
103129
uint64_t ChunkIdx;
104130
};
105131

106132
// Return the state of a counter. This only works for set counters.
107-
static CounterState getCounterState(unsigned ID) {
108-
auto &Us = instance();
109-
auto Result = Us.Counters.find(ID);
110-
assert(Result != Us.Counters.end() && "Asking about a non-set counter");
111-
return {Result->second.Count, Result->second.CurrChunkIdx};
133+
static CounterState getCounterState(CounterInfo &Info) {
134+
return {Info.Count, Info.CurrChunkIdx};
112135
}
113136

114137
// Set a registered counter to a given state.
115-
static void setCounterState(unsigned ID, CounterState State) {
116-
auto &Us = instance();
117-
auto &Counter = Us.Counters[ID];
118-
Counter.Count = State.Count;
119-
Counter.CurrChunkIdx = State.ChunkIdx;
138+
static void setCounterState(CounterInfo &Info, CounterState State) {
139+
Info.Count = State.Count;
140+
Info.CurrChunkIdx = State.ChunkIdx;
120141
}
121142

122143
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -126,66 +147,38 @@ class DebugCounter {
126147

127148
LLVM_ABI void print(raw_ostream &OS) const;
128149

129-
// Get the counter ID for a given named counter, or return 0 if none is found.
130-
unsigned getCounterId(const std::string &Name) const {
131-
return RegisteredCounters.idFor(Name);
150+
// Get the counter info for a given named counter,
151+
// or return null if none is found.
152+
CounterInfo *getCounterInfo(StringRef Name) const {
153+
return Counters.lookup(Name);
132154
}
133155

134156
// Return the number of registered counters.
135-
unsigned int getNumCounters() const { return RegisteredCounters.size(); }
157+
unsigned int getNumCounters() const { return Counters.size(); }
136158

137-
// Return the name and description of the counter with the given ID.
138-
std::pair<std::string, std::string> getCounterInfo(unsigned ID) const {
139-
return {RegisteredCounters[ID], Counters.lookup(ID).Desc};
159+
// Return the name and description of the counter with the given info.
160+
std::pair<StringRef, StringRef> getCounterDesc(CounterInfo *Info) const {
161+
return {Info->Name, Info->Desc};
140162
}
141163

142164
// Iterate through the registered counters
143-
using CounterVector = UniqueVector<std::string>;
144-
CounterVector::const_iterator begin() const {
145-
return RegisteredCounters.begin();
165+
MapVector<StringRef, CounterInfo *>::const_iterator begin() const {
166+
return Counters.begin();
167+
}
168+
MapVector<StringRef, CounterInfo *>::const_iterator end() const {
169+
return Counters.end();
146170
}
147-
CounterVector::const_iterator end() const { return RegisteredCounters.end(); }
148-
149-
// Force-enables counting all DebugCounters.
150-
//
151-
// Since DebugCounters are incompatible with threading (not only do they not
152-
// make sense, but we'll also see data races), this should only be used in
153-
// contexts where we're certain we won't spawn threads.
154-
static void enableAllCounters() { instance().Enabled = true; }
155171

156-
static bool isCountingEnabled() {
157-
// Compile to nothing when debugging is off
158-
#ifdef NDEBUG
159-
return false;
160-
#else
161-
return instance().Enabled;
162-
#endif
172+
void activateAllCounters() {
173+
for (auto &[_, Counter] : Counters)
174+
Counter->Active = true;
163175
}
164176

165177
protected:
166-
unsigned addCounter(const std::string &Name, const std::string &Desc) {
167-
unsigned Result = RegisteredCounters.insert(Name);
168-
auto &C = Counters[Result];
169-
C = {};
170-
C.Desc = Desc;
171-
return Result;
172-
}
173-
// Struct to store counter info.
174-
struct CounterInfo {
175-
int64_t Count = 0;
176-
uint64_t CurrChunkIdx = 0;
177-
bool IsSet = false;
178-
std::string Desc;
179-
SmallVector<Chunk> Chunks;
180-
};
178+
void addCounter(CounterInfo *Info) { Counters[Info->Name] = Info; }
181179
bool handleCounterIncrement(CounterInfo &Info);
182180

183-
DenseMap<unsigned, CounterInfo> Counters;
184-
CounterVector RegisteredCounters;
185-
186-
// Whether we should do DebugCounting at all. DebugCounters aren't
187-
// thread-safe, so this should always be false in multithreaded scenarios.
188-
bool Enabled = false;
181+
MapVector<StringRef, CounterInfo *> Counters;
189182

190183
bool ShouldPrintCounter = false;
191184

@@ -195,8 +188,7 @@ class DebugCounter {
195188
};
196189

197190
#define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \
198-
static const unsigned VARNAME = \
199-
DebugCounter::registerCounter(COUNTERNAME, DESC)
191+
static DebugCounter::CounterInfo VARNAME(COUNTERNAME, DESC)
200192

201193
} // namespace llvm
202194
#endif

llvm/lib/Support/DebugCounter.cpp

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "llvm/Support/CommandLine.h"
66
#include "llvm/Support/Format.h"
7+
#include "llvm/Support/ManagedStatic.h"
78

89
using namespace llvm;
910

@@ -110,12 +111,11 @@ class DebugCounterList : public cl::list<std::string, DebugCounter> {
110111
// width, so we do the same.
111112
Option::printHelpStr(HelpStr, GlobalWidth, ArgStr.size() + 6);
112113
const auto &CounterInstance = DebugCounter::instance();
113-
for (const auto &Name : CounterInstance) {
114-
const auto Info =
115-
CounterInstance.getCounterInfo(CounterInstance.getCounterId(Name));
116-
size_t NumSpaces = GlobalWidth - Info.first.size() - 8;
117-
outs() << " =" << Info.first;
118-
outs().indent(NumSpaces) << " - " << Info.second << '\n';
114+
for (const auto &Entry : CounterInstance) {
115+
const auto &[Name, Desc] = CounterInstance.getCounterDesc(Entry.second);
116+
size_t NumSpaces = GlobalWidth - Name.size() - 8;
117+
outs() << " =" << Name;
118+
outs().indent(NumSpaces) << " - " << Desc << '\n';
119119
}
120120
}
121121
};
@@ -138,7 +138,7 @@ struct DebugCounterOwner : DebugCounter {
138138
cl::desc("Print out debug counter info after all counters accumulated"),
139139
cl::callback([&](const bool &Value) {
140140
if (Value)
141-
enableAllCounters();
141+
activateAllCounters();
142142
})};
143143
cl::opt<bool, true> PrintDebugCounterQueries{
144144
"print-debug-counter-queries",
@@ -171,12 +171,14 @@ struct DebugCounterOwner : DebugCounter {
171171

172172
} // anonymous namespace
173173

174+
// Use ManagedStatic instead of function-local static variable to ensure
175+
// the destructor (which accesses counters and streams) runs during
176+
// llvm_shutdown() rather than at some unspecified point.
177+
static ManagedStatic<DebugCounterOwner> Owner;
178+
174179
void llvm::initDebugCounterOptions() { (void)DebugCounter::instance(); }
175180

176-
DebugCounter &DebugCounter::instance() {
177-
static DebugCounterOwner O;
178-
return O;
179-
}
181+
DebugCounter &DebugCounter::instance() { return *Owner; }
180182

181183
// This is called by the command line parser when it sees a value for the
182184
// debug-counter option defined above.
@@ -202,32 +204,26 @@ void DebugCounter::push_back(const std::string &Val) {
202204
return;
203205
}
204206

205-
unsigned CounterID = getCounterId(std::string(CounterName));
206-
if (!CounterID) {
207+
CounterInfo *Counter = getCounterInfo(CounterName);
208+
if (!Counter) {
207209
errs() << "DebugCounter Error: " << CounterName
208210
<< " is not a registered counter\n";
209211
return;
210212
}
211-
enableAllCounters();
212213

213-
CounterInfo &Counter = Counters[CounterID];
214-
Counter.IsSet = true;
215-
Counter.Chunks = std::move(Chunks);
214+
Counter->Active = Counter->IsSet = true;
215+
Counter->Chunks = std::move(Chunks);
216216
}
217217

218218
void DebugCounter::print(raw_ostream &OS) const {
219-
SmallVector<StringRef, 16> CounterNames(RegisteredCounters.begin(),
220-
RegisteredCounters.end());
219+
SmallVector<StringRef, 16> CounterNames(Counters.keys());
221220
sort(CounterNames);
222221

223-
auto &Us = instance();
224222
OS << "Counters and values:\n";
225-
for (auto &CounterName : CounterNames) {
226-
unsigned CounterID = getCounterId(std::string(CounterName));
227-
const CounterInfo &C = Us.Counters[CounterID];
228-
OS << left_justify(RegisteredCounters[CounterID], 32) << ": {" << C.Count
229-
<< ",";
230-
printChunks(OS, C.Chunks);
223+
for (StringRef CounterName : CounterNames) {
224+
const CounterInfo *C = getCounterInfo(CounterName);
225+
OS << left_justify(C->Name, 32) << ": {" << C->Count << ",";
226+
printChunks(OS, C->Chunks);
231227
OS << "}\n";
232228
}
233229
}
@@ -257,20 +253,14 @@ bool DebugCounter::handleCounterIncrement(CounterInfo &Info) {
257253
return Res;
258254
}
259255

260-
bool DebugCounter::shouldExecuteImpl(unsigned CounterName) {
256+
bool DebugCounter::shouldExecuteImpl(CounterInfo &Counter) {
261257
auto &Us = instance();
262-
auto Result = Us.Counters.find(CounterName);
263-
if (Result != Us.Counters.end()) {
264-
auto &CounterInfo = Result->second;
265-
bool Res = Us.handleCounterIncrement(CounterInfo);
266-
if (Us.ShouldPrintCounterQueries && CounterInfo.IsSet) {
267-
dbgs() << "DebugCounter " << Us.RegisteredCounters[CounterName] << "="
268-
<< (CounterInfo.Count - 1) << (Res ? " execute" : " skip") << "\n";
269-
}
270-
return Res;
258+
bool Res = Us.handleCounterIncrement(Counter);
259+
if (Us.ShouldPrintCounterQueries && Counter.IsSet) {
260+
dbgs() << "DebugCounter " << Counter.Name << "=" << (Counter.Count - 1)
261+
<< (Res ? " execute" : " skip") << "\n";
271262
}
272-
// Didn't find the counter, should we warn?
273-
return true;
263+
return Res;
274264
}
275265

276266
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)

llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
#include <cassert>
9696
#include <cstdint>
9797
#include <iterator>
98+
#include <map>
9899
#include <memory>
99100
#include <optional>
100101
#include <set>

0 commit comments

Comments
 (0)