Skip to content

Commit 539eaca

Browse files
committed
AST: Per-request dependency maps
Just as with the result cache, instead of a single DenseMap with type-erased AnyRequest keys, we can use per-request maps for a nice performance improvement.
1 parent 9258826 commit 539eaca

File tree

3 files changed

+141
-8
lines changed

3 files changed

+141
-8
lines changed

include/swift/AST/Evaluator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ class Evaluator {
323323
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
324324
void clearCachedOutput(const Request &request) {
325325
cache.erase<Request>(request);
326+
recorder.clearRequest<Request>(request);
326327
}
327328

328329
/// Clear the cache stored within this evaluator.

include/swift/AST/EvaluatorDependencies.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "swift/AST/AnyRequest.h"
2222
#include "swift/AST/DependencyCollector.h"
23+
#include "swift/AST/RequestCache.h"
2324
#include "swift/Basic/NullablePtr.h"
2425
#include "llvm/ADT/DenseMap.h"
2526
#include "llvm/ADT/DenseSet.h"
@@ -63,8 +64,7 @@ struct DependencyRecorder {
6364
/// downstream request as well. Note that uncached requests don't appear as
6465
/// keys in this map; their references are charged to the innermost cached
6566
/// active request.
66-
llvm::DenseMap<AnyRequest, std::vector<DependencyCollector::Reference>>
67-
requestReferences;
67+
RequestReferences requestReferences;
6868

6969
/// Stack of references from each cached active request. When evaluating a
7070
/// dependency sink request, we update the innermost set of references.
@@ -103,6 +103,10 @@ struct DependencyRecorder {
103103
void handleDependencySourceRequest(const Request &req,
104104
SourceFile *source);
105105

106+
/// Clear the recorded dependencies of a request, if any.
107+
template<typename Request>
108+
void clearRequest(const Request &req);
109+
106110
private:
107111
/// Add an entry to the innermost set on the activeRequestReferences stack.
108112
/// Called from the DependencyCollector.
@@ -159,7 +163,7 @@ void evaluator::DependencyRecorder::endRequest(const Request &req) {
159163

160164
// Finally, record the dependencies so we can replay them
161165
// later when the request is re-evaluated.
162-
requestReferences.insert({AnyRequest(req), std::move(vec)});
166+
requestReferences.insert<Request>(std::move(req), std::move(vec));
163167
}
164168

165169
template<typename Request>
@@ -169,8 +173,8 @@ void evaluator::DependencyRecorder::replayCachedRequest(const Request &req) {
169173
if (activeRequestReferences.empty())
170174
return;
171175

172-
auto found = requestReferences.find_as(req);
173-
if (found == requestReferences.end())
176+
auto found = requestReferences.find_as<Request>(req);
177+
if (found == requestReferences.end<Request>())
174178
return;
175179

176180
activeRequestReferences.back().insert(found->second.begin(),
@@ -181,13 +185,19 @@ template<typename Request>
181185
void evaluator::DependencyRecorder::handleDependencySourceRequest(
182186
const Request &req,
183187
SourceFile *source) {
184-
auto found = requestReferences.find_as(req);
185-
if (found != requestReferences.end()) {
188+
auto found = requestReferences.find_as<Request>(req);
189+
if (found != requestReferences.end<Request>()) {
186190
fileReferences[source].insert(found->second.begin(),
187191
found->second.end());
188192
}
189193
}
190194

195+
template<typename Request>
196+
void evaluator::DependencyRecorder::clearRequest(
197+
const Request &req) {
198+
requestReferences.erase(req);
199+
}
200+
191201
} // end namespace evaluator
192202

193203
} // end namespace swift

include/swift/AST/RequestCache.h

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
//===----------------------------------------------------------------------===//
1212
//
1313
// This file defines data structures to efficiently support the request
14-
// evaluator's request caching.
14+
// evaluator's per-request caching and dependency tracking maps.
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "swift/AST/DependencyCollector.h"
1819
#include "llvm/ADT/DenseMap.h"
1920
#include "llvm/ADT/StringRef.h"
2021

@@ -279,6 +280,127 @@ class RequestCache {
279280
}
280281
};
281282

283+
/// Type-erased wrapper for caching dependencies from a single type of request.
284+
class PerRequestReferences {
285+
void *Storage;
286+
std::function<void(void *)> Deleter;
287+
288+
PerRequestReferences(void *storage, std::function<void(void *)> deleter)
289+
: Storage(storage), Deleter(deleter) {}
290+
291+
public:
292+
PerRequestReferences() : Storage(nullptr), Deleter([](void *) {}) {}
293+
PerRequestReferences(PerRequestReferences &&other)
294+
: Storage(other.Storage), Deleter(std::move(other.Deleter)) {
295+
other.Storage = nullptr;
296+
}
297+
298+
PerRequestReferences &operator=(PerRequestReferences &&other) {
299+
if (&other != this) {
300+
this->~PerRequestReferences();
301+
new (this) PerRequestReferences(std::move(other));
302+
}
303+
return *this;
304+
}
305+
306+
PerRequestReferences(const PerRequestReferences &) = delete;
307+
PerRequestReferences &operator=(const PerRequestReferences &) = delete;
308+
309+
template <typename Request>
310+
static PerRequestReferences makeEmpty() {
311+
using Map =
312+
llvm::DenseMap<RequestKey<Request>,
313+
std::vector<DependencyCollector::Reference>>;
314+
return PerRequestReferences(new Map(),
315+
[](void *ptr) { delete static_cast<Map *>(ptr); });
316+
}
317+
318+
template <typename Request>
319+
llvm::DenseMap<RequestKey<Request>,
320+
std::vector<DependencyCollector::Reference>> *
321+
get() const {
322+
using Map =
323+
llvm::DenseMap<RequestKey<Request>,
324+
std::vector<DependencyCollector::Reference>>;
325+
assert(Storage);
326+
return static_cast<Map *>(Storage);
327+
}
328+
329+
bool isNull() const { return !Storage; }
330+
~PerRequestReferences() {
331+
if (Storage)
332+
Deleter(Storage);
333+
}
334+
};
335+
336+
/// Data structure for caching dependencies from requests. Sharded by the
337+
/// type ID zone and request kind, with a PerRequestReferences for each
338+
/// request kind.
339+
///
340+
/// Conceptually equivalent to DenseMap<AnyRequest, vector<Reference>>, but
341+
/// without type erasure overhead for keys.
342+
class RequestReferences {
343+
344+
#define SWIFT_TYPEID_ZONE(Name, Id) \
345+
std::vector<PerRequestReferences> Name##ZoneRefs; \
346+
\
347+
template < \
348+
typename Request, typename ZoneTypes = TypeIDZoneTypes<Zone::Name>, \
349+
typename std::enable_if<TypeID<Request>::zone == Zone::Name>::type * = \
350+
nullptr> \
351+
llvm::DenseMap<RequestKey<Request>, \
352+
std::vector<DependencyCollector::Reference>> * \
353+
getRefs() { \
354+
auto &refs = Name##ZoneRefs; \
355+
if (refs.empty()) { \
356+
refs.resize(ZoneTypes::Count); \
357+
} \
358+
auto idx = TypeID<Request>::localID; \
359+
if (refs[idx].isNull()) { \
360+
refs[idx] = PerRequestReferences::makeEmpty<Request>(); \
361+
} \
362+
return refs[idx].template get<Request>(); \
363+
}
364+
#include "swift/Basic/TypeIDZones.def"
365+
#undef SWIFT_TYPEID_ZONE
366+
367+
public:
368+
template <typename Request>
369+
typename llvm::DenseMap<RequestKey<Request>,
370+
std::vector<DependencyCollector::Reference>>::const_iterator
371+
find_as(const Request &req) {
372+
auto *refs = getRefs<Request>();
373+
return refs->find_as(req);
374+
}
375+
376+
template <typename Request>
377+
typename llvm::DenseMap<RequestKey<Request>,
378+
std::vector<DependencyCollector::Reference>>::const_iterator
379+
end() {
380+
auto *refs = getRefs<Request>();
381+
return refs->end();
382+
}
383+
384+
template <typename Request>
385+
void insert(Request req, std::vector<DependencyCollector::Reference> val) {
386+
auto *refs = getRefs<Request>();
387+
refs->insert({RequestKey<Request>(std::move(req)),
388+
std::move(val)});
389+
}
390+
391+
template <typename Request>
392+
void erase(Request req) {
393+
auto *refs = getRefs<Request>();
394+
refs->erase(RequestKey<Request>(std::move(req)));
395+
}
396+
397+
void clear() {
398+
#define SWIFT_TYPEID_ZONE(Name, Id) Name##ZoneRefs.clear();
399+
#include "swift/Basic/TypeIDZones.def"
400+
#undef SWIFT_TYPEID_ZONE
401+
}
402+
};
403+
282404
} // end namespace evaluator
283405

284406
} // end namespace swift

0 commit comments

Comments
 (0)