Files
swift-mirror/stdlib/runtime/MetadataCache.h
Nadav Rotem dbcdfff724 Remove the locks from getGenericMetadata to accelerate Debug builds.
This commit removes the locks from the family of getXXXXMetadata APIs in the
fast path that does not create a new metadata. We still need the lock for the
cache-miss case because constructing metadata has side effects and we must
not construct two metadatas at the same time.

I am seeing a 25% - 30% boost in performance on most workloads that are built
with -Onone.

Swift SVN r23353
2014-11-15 07:07:38 +00:00

219 lines
6.8 KiB
C++

//===--- MetadataCache.h - Implements the metadata cache -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_RUNTIME_METADATACACHE_H
#define SWIFT_RUNTIME_METADATACACHE_H
#include "llvm/ADT/Hashing.h"
#include "Locks.h"
#include "Concurrent.h"
#include <mutex>
#include <condition_variable>
#ifndef SWIFT_DEBUG_RUNTIME
#define SWIFT_DEBUG_RUNTIME 0
#endif
static void *permanentAlloc(size_t size) { return malloc(size); }
// A wrapper around a pointer to a metadata cache entry that provides
// DenseMap semantics that compare values in the key vector for the metadata
// instance.
//
// This is stored as a pointer to the arguments buffer, so that we can save
// an offset while looking for the matching argument given a key.
template<class Entry>
class EntryRef {
const void * const *args;
unsigned length;
EntryRef(const void * const *args, unsigned length) :
args(args), length(length) {}
public:
static EntryRef forEntry(const Entry *e, unsigned numArguments) {
return EntryRef(e->getArgumentsBuffer(), numArguments);
}
static EntryRef forArguments(const void * const *args,
unsigned numArguments) {
return EntryRef(args, numArguments);
}
const Entry *getEntry() const {
return Entry::fromArgumentsBuffer(args, length);
}
bool operator==(EntryRef<Entry>& rhs) const {
// Compare the sizes.
unsigned asize = size(), bsize = rhs.size();
if (asize != bsize) return false;
// Compare the content.
auto abegin = begin(), bbegin = rhs.begin();
for (unsigned i = 0; i < asize; ++i)
if (abegin[i] != bbegin[i]) return false;
return true;
}
const void * const *begin() const { return args; }
const void * const *end() const { return args + length; }
unsigned size() const { return length; }
};
template <class Impl>
struct CacheEntryHeader {
/// LLDB walks this list.
const Impl *Next;
};
/// A CRTP class for defining entries in a metadata cache.
template <class Impl, class Header = CacheEntryHeader<Impl> >
class alignas(void*) CacheEntry : public Header {
CacheEntry(const CacheEntry &other) = delete;
void operator=(const CacheEntry &other) = delete;
Impl *asImpl() { return static_cast<Impl*>(this); }
const Impl *asImpl() const { return static_cast<const Impl*>(this); }
protected:
CacheEntry() = default;
public:
static Impl *allocate(const void * const *arguments,
size_t numArguments, size_t payloadSize) {
void *buffer = permanentAlloc(sizeof(Impl) +
numArguments * sizeof(void*) +
payloadSize);
void *resultPtr = (char*)buffer + numArguments * sizeof(void*);
auto result = new (resultPtr) Impl(numArguments);
// Copy the arguments into the right place for the key.
memcpy(buffer, arguments,
numArguments * sizeof(void*));
return result;
}
void **getArgumentsBuffer() {
return reinterpret_cast<void**>(this) - asImpl()->getNumArguments();
}
void * const *getArgumentsBuffer() const {
return reinterpret_cast<void * const *>(this)
- asImpl()->getNumArguments();
}
template <class T> T *getData() {
return reinterpret_cast<T *>(asImpl() + 1);
}
template <class T> const T *getData() const {
return const_cast<CacheEntry*>(this)->getData<T>();
}
static const Impl *fromArgumentsBuffer(const void * const *argsBuffer,
unsigned numArguments) {
return reinterpret_cast<const Impl *>(argsBuffer + numArguments);
}
};
/// The implementation of a metadata cache. Note that all-zero must
/// be a valid state for the cache.
template <class Entry> class MetadataCache {
struct EntryPair {
EntryPair(EntryRef<Entry> K, Entry* V) : Key(K), Value(V) {}
EntryRef<Entry> Key;
Entry* Value;
};
ConcurrentMap<llvm::hash_code, ConcurrentList<EntryPair>> Map;
/// Synchronization of metadata creation.
std::mutex Lock;
/// The head of a linked list connecting all the metadata cache entries.
/// TODO: Remove this when LLDB is able to understand the final data
/// structure for the metadata cache.
const Entry *Head;
public:
MetadataCache() : Lock() {}
~MetadataCache() { }
/// Caches are not copyable.
MetadataCache(const MetadataCache &other) = delete;
MetadataCache &operator=(const MetadataCache &other) = delete;
/// Look up a cached metadata entry. If a cache match exists, return it.
/// Otherwise, call entryBuilder() and add that to the cache.
const Entry *findOrAdd(const void * const *arguments, size_t numArguments,
std::function<Entry *()> entryBuilder) {
#if SWIFT_DEBUG_RUNTIME
printf("%s(%p): looking for entry with %zu arguments:\n",
Entry::getName(), this, numArguments);
for (size_t i = 0; i < numArguments; i++) {
printf("%s(%p): %p\n", Entry::getName(), this, arguments[i]);
}
#endif
EntryRef<Entry> key = EntryRef<Entry>::forArguments(arguments,numArguments);
llvm::hash_code hash = llvm::hash_combine_range(key.begin(), key.end());
#if SWIFT_DEBUG_RUNTIME
printf("%s(%p): generated hash %llx\n",
Entry::getName(), this, hash);
#endif
// Look for an existing entry.
// Find the bucket for the metadata entry.
ConcurrentList<EntryPair> &Bucket = Map.findOrAllocateNode(hash);
// Scan the bucket and return the value if we found the key.
for (auto &A : Bucket) {
if (A.Key == key) return A.Value;
}
// We did not find a key so we will need to create one and store it.
{
std::unique_lock<std::mutex> ConstructionGuard(Lock);
// Some other thread may have setup the value we are about to construct
// while we were asleep so do a search before constructing a new value.
for (auto &A : Bucket) {
if (A.Key == key) return A.Value;
}
// Build the new cache entry.
// For some cache types this call may re-entrantly perform additional
// cache lookups.
Entry *entry = entryBuilder();
assert(entry);
// Update the linked list.
entry->Next = Head;
Head = entry;
key = EntryRef<Entry>::forEntry(entry, entry->getNumArguments());
Bucket.push(EntryPair(key, entry));
#if SWIFT_DEBUG_RUNTIME
printf("%s(%p): created %p\n",
Entry::getName(), this, entry);
#endif
return key.getEntry();
}
}
};
#endif // SWIFT_RUNTIME_METADATACACHE_H