Reimplement LRUCache, fold away LRUSnapshotCache, add tests.
LRUCache previously had a complexity of O(n).
Reimplement using a `std::unordered_set` and a linked list to get this reduced to O(1).
Renamed `LRUCache::query()` to `LRUCache::get()`, as this is a more common verb for a cache, and the `query()` suggests it is side effect free (when it actually makes the entry MRU).
Move `LRUCache.hpp` from `src/Device` to `src/System` so it can be tested by `system-unittests`.
Move the logic of `LRUSnapshotCache` into `VkDevice::SamplingRoutineCache`, as this was the only place it was used, and made it exceptionally hard to separate mutex-locked data from non-locked data.
This is part of the work to get our code statically thread-safe-verifiable.
Bug: b/153194656
Change-Id: Ie02888ae6c7ed4066df77d692dfae28c3bc1664d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43489
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index 88f11d9..aea4e62 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -1682,7 +1682,7 @@
Blitter::BlitRoutineType Blitter::getBlitRoutine(const State &state)
{
std::unique_lock<std::mutex> lock(blitMutex);
- auto blitRoutine = blitCache.query(state);
+ auto blitRoutine = blitCache.lookup(state);
if(!blitRoutine)
{
@@ -1696,7 +1696,7 @@
Blitter::CornerUpdateRoutineType Blitter::getCornerUpdateRoutine(const State &state)
{
std::unique_lock<std::mutex> lock(cornerUpdateMutex);
- auto cornerUpdateRoutine = cornerUpdateCache.query(state);
+ auto cornerUpdateRoutine = cornerUpdateCache.lookup(state);
if(!cornerUpdateRoutine)
{
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 50b7e29..8735feb 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -95,6 +95,7 @@
int destSamples = 0;
bool filter3D = false;
};
+ friend std::hash<Blitter::State>;
struct BlitData
{
@@ -193,4 +194,22 @@
} // namespace sw
+namespace std {
+
+template<>
+struct hash<sw::Blitter::State>
+{
+ uint64_t operator()(const sw::Blitter::State &state) const
+ {
+ uint64_t hash = state.sourceFormat;
+ hash = hash * 31 + state.destFormat;
+ hash = hash * 31 + state.srcSamples;
+ hash = hash * 31 + state.destSamples;
+ hash = hash * 31 + state.filter3D;
+ return hash;
+ }
+};
+
+} // namespace std
+
#endif // sw_Blitter_hpp
diff --git a/src/Device/CMakeLists.txt b/src/Device/CMakeLists.txt
index fae3be4..d7ca09c 100644
--- a/src/Device/CMakeLists.txt
+++ b/src/Device/CMakeLists.txt
@@ -31,7 +31,6 @@
Context.hpp
ETC_Decoder.cpp
ETC_Decoder.hpp
- LRUCache.hpp
Memset.hpp
PixelProcessor.cpp
PixelProcessor.hpp
diff --git a/src/Device/LRUCache.hpp b/src/Device/LRUCache.hpp
deleted file mode 100644
index 848d421..0000000
--- a/src/Device/LRUCache.hpp
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef sw_LRUCache_hpp
-#define sw_LRUCache_hpp
-
-#include "System/Math.hpp"
-
-#include <type_traits>
-#include <unordered_map>
-
-namespace sw {
-
-template<class Key, class Data>
-class LRUCache
-{
-public:
- LRUCache(int n);
-
- virtual ~LRUCache();
-
- Data query(const Key &key) const;
- virtual Data add(const Key &key, const Data &data);
-
- int getSize() { return size; }
- Key &getKey(int i) { return key[i]; }
-
-protected:
- int size;
- int mask;
- int top;
- int fill;
-
- Key *key;
- Key **ref;
- Data *data;
-};
-
-// An LRU cache which can take a 'snapshot' of its current state. This is useful
-// for allowing concurrent read access without requiring a mutex to be locked.
-template<class Key, class Data, class Hasher = std::hash<Key>>
-class LRUSnapshotCache : public LRUCache<Key, Data>
-{
- using LRUBase = LRUCache<Key, Data>;
-
-public:
- LRUSnapshotCache(int n)
- : LRUBase(n)
- {}
- ~LRUSnapshotCache() { clearSnapshot(); }
-
- Data add(const Key &key, const Data &data) override
- {
- snapshotNeedsUpdate = true;
- return LRUBase::add(key, data);
- }
-
- void updateSnapshot();
- const Data &querySnapshot(const Key &key) const;
-
-private:
- void clearSnapshot();
- bool snapshotNeedsUpdate = false;
- std::unordered_map<Key, Data, Hasher> snapshot;
-};
-
-} // namespace sw
-
-namespace sw {
-
-template<class Key, class Data>
-LRUCache<Key, Data>::LRUCache(int n)
-{
- size = ceilPow2(n);
- mask = size - 1;
- top = 0;
- fill = 0;
-
- key = new Key[size];
- ref = new Key *[size];
- data = new Data[size];
-
- for(int i = 0; i < size; i++)
- {
- ref[i] = &key[i];
- }
-}
-
-template<class Key, class Data>
-LRUCache<Key, Data>::~LRUCache()
-{
- delete[] key;
- key = nullptr;
-
- delete[] ref;
- ref = nullptr;
-
- delete[] data;
- data = nullptr;
-}
-
-template<class Key, class Data>
-Data LRUCache<Key, Data>::query(const Key &key) const
-{
- for(int i = top; i > top - fill; i--)
- {
- int j = i & mask;
-
- if(key == *ref[j])
- {
- Data hit = data[j];
-
- if(i != top)
- {
- // Move one up
- int k = (j + 1) & mask;
-
- Data swapD = data[k];
- data[k] = data[j];
- data[j] = swapD;
-
- Key *swapK = ref[k];
- ref[k] = ref[j];
- ref[j] = swapK;
- }
-
- return hit;
- }
- }
-
- return {}; // Not found
-}
-
-template<class Key, class Data>
-Data LRUCache<Key, Data>::add(const Key &key, const Data &data)
-{
- top = (top + 1) & mask;
- fill = fill + 1 < size ? fill + 1 : size;
-
- *ref[top] = key;
- this->data[top] = data;
-
- return data;
-}
-
-template<class Key, class Data, class Hasher>
-void LRUSnapshotCache<Key, Data, Hasher>::clearSnapshot()
-{
- snapshot.clear();
-}
-
-template<class Key, class Data, class Hasher>
-void LRUSnapshotCache<Key, Data, Hasher>::updateSnapshot()
-{
- if(snapshotNeedsUpdate)
- {
- clearSnapshot();
-
- for(int i = 0; i < LRUBase::size; i++)
- {
- if(LRUBase::data[i])
- {
- snapshot[*LRUBase::ref[i]] = LRUBase::data[i];
- }
- }
-
- snapshotNeedsUpdate = false;
- }
-}
-
-template<class Key, class Data, class Hasher>
-const Data &LRUSnapshotCache<Key, Data, Hasher>::querySnapshot(const Key &key) const
-{
- auto it = snapshot.find(key);
- static Data null = {};
- return (it != snapshot.end()) ? it->second : null;
-}
-
-} // namespace sw
-
-#endif // sw_LRUCache_hpp
diff --git a/src/Device/PixelProcessor.cpp b/src/Device/PixelProcessor.cpp
index 1e25645..6d49315 100644
--- a/src/Device/PixelProcessor.cpp
+++ b/src/Device/PixelProcessor.cpp
@@ -153,7 +153,7 @@
SpirvShader const *pixelShader,
const vk::DescriptorSet::Bindings &descriptorSets)
{
- auto routine = routineCache->query(state);
+ auto routine = routineCache->lookup(state);
if(!routine)
{
diff --git a/src/Device/PixelProcessor.hpp b/src/Device/PixelProcessor.hpp
index c140e0c..3ba54d8 100644
--- a/src/Device/PixelProcessor.hpp
+++ b/src/Device/PixelProcessor.hpp
@@ -164,4 +164,17 @@
} // namespace sw
+namespace std {
+
+template<>
+struct hash<sw::PixelProcessor::State>
+{
+ uint64_t operator()(const sw::PixelProcessor::State &state) const
+ {
+ return state.hash;
+ }
+};
+
+} // namespace std
+
#endif // sw_PixelProcessor_hpp
diff --git a/src/Device/RoutineCache.hpp b/src/Device/RoutineCache.hpp
index 9bf2e31..71046c3 100644
--- a/src/Device/RoutineCache.hpp
+++ b/src/Device/RoutineCache.hpp
@@ -15,7 +15,7 @@
#ifndef sw_RoutineCache_hpp
#define sw_RoutineCache_hpp
-#include "LRUCache.hpp"
+#include "System/LRUCache.hpp"
#include "Reactor/Reactor.hpp"
diff --git a/src/Device/SetupProcessor.cpp b/src/Device/SetupProcessor.cpp
index 19e7843..bda0df0 100644
--- a/src/Device/SetupProcessor.cpp
+++ b/src/Device/SetupProcessor.cpp
@@ -100,7 +100,7 @@
SetupProcessor::RoutineType SetupProcessor::routine(const State &state)
{
- auto routine = routineCache->query(state);
+ auto routine = routineCache->lookup(state);
if(!routine)
{
diff --git a/src/Device/SetupProcessor.hpp b/src/Device/SetupProcessor.hpp
index b8b6d84..c86bb00 100644
--- a/src/Device/SetupProcessor.hpp
+++ b/src/Device/SetupProcessor.hpp
@@ -85,4 +85,17 @@
} // namespace sw
+namespace std {
+
+template<>
+struct hash<sw::SetupProcessor::State>
+{
+ uint64_t operator()(const sw::SetupProcessor::State &state) const
+ {
+ return state.hash;
+ }
+};
+
+} // namespace std
+
#endif // sw_SetupProcessor_hpp
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index a2a33c5..54d5df8 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -98,7 +98,7 @@
SpirvShader const *vertexShader,
const vk::DescriptorSet::Bindings &descriptorSets)
{
- auto routine = routineCache->query(state);
+ auto routine = routineCache->lookup(state);
if(!routine) // Create one
{
diff --git a/src/Device/VertexProcessor.hpp b/src/Device/VertexProcessor.hpp
index 739ffc6..d8acdd9 100644
--- a/src/Device/VertexProcessor.hpp
+++ b/src/Device/VertexProcessor.hpp
@@ -106,4 +106,17 @@
} // namespace sw
+namespace std {
+
+template<>
+struct hash<sw::VertexProcessor::State>
+{
+ uint64_t operator()(const sw::VertexProcessor::State &state) const
+ {
+ return state.hash;
+ }
+};
+
+} // namespace std
+
#endif // sw_VertexProcessor_hpp
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index dd84e70..f1a44ef 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -40,11 +40,6 @@
ASSERT(imageDescriptor->device);
- if(auto routine = imageDescriptor->device->querySnapshotCache(key))
- {
- return (ImageSampler *)(routine->getEntry());
- }
-
vk::Device::SamplingRoutineCache *cache = imageDescriptor->device->getSamplingRoutineCache();
auto createSamplingRoutine = [&](const vk::Device::SamplingRoutineCache::Key &key) {
diff --git a/src/System/BUILD.gn b/src/System/BUILD.gn
index c90c835..4595028 100644
--- a/src/System/BUILD.gn
+++ b/src/System/BUILD.gn
@@ -21,6 +21,7 @@
"Configurator.hpp",
"Debug.hpp",
"Half.hpp",
+ "LRUCache.hpp",
"Math.hpp",
"Memory.hpp",
"Socket.cpp",
diff --git a/src/System/CMakeLists.txt b/src/System/CMakeLists.txt
index d7d5f00..bb5f305 100644
--- a/src/System/CMakeLists.txt
+++ b/src/System/CMakeLists.txt
@@ -28,6 +28,7 @@
Debug.hpp
Half.cpp
Half.hpp
+ LRUCache.hpp
Math.cpp
Math.hpp
Memory.cpp
diff --git a/src/System/LRUCache.hpp b/src/System/LRUCache.hpp
new file mode 100644
index 0000000..ff18671
--- /dev/null
+++ b/src/System/LRUCache.hpp
@@ -0,0 +1,357 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef sw_LRUCache_hpp
+#define sw_LRUCache_hpp
+
+#include "System/Debug.hpp"
+
+#include <cstddef>
+#include <functional>
+#include <unordered_set>
+#include <vector>
+
+namespace sw {
+
+// LRUCache is a least recently used cache of a fixed capacity.
+template<typename KEY, typename DATA, typename HASH = std::hash<KEY> >
+class LRUCache
+{
+ struct Entry;
+
+public:
+ using Key = KEY;
+ using Data = DATA;
+ using Hash = HASH;
+
+ // view is a const accessor of a single cache entry.
+ class view
+ {
+ public:
+ inline view(Entry *);
+
+ inline const Key &key() const;
+ inline const Data &data() const;
+
+ private:
+ Entry *entry;
+ };
+
+ class iterator
+ {
+ public:
+ inline iterator(Entry *);
+ inline view operator*() const;
+ inline iterator &operator++();
+ inline bool operator==(const iterator &) const;
+ inline bool operator!=(const iterator &) const;
+
+ private:
+ Entry *entry;
+ };
+
+ // Construct a LRU cache with the given maximum number of entries.
+ inline LRUCache(size_t capacity);
+ inline ~LRUCache() = default;
+
+ // lookup() looks up the cache entry with the given key.
+ // If the entry is found, this is moved to the most-recent position in the
+ // cache, and its data is returned.
+ // If the entry is not found, then a default initialized Data is returned.
+ inline Data lookup(const Key &key);
+
+ // add() adds the data to the cache using the given key, placed at the
+ // most-recent position in the cache.
+ // If an existing entry exists in the cache with the given key, then this is
+ // replaced with data.
+ // If no existing entry exists in the cache, and the cache is already full
+ // then the least recently used entry is evicted before adding the new
+ // entry.
+ inline void add(const Key &key, const Data &data);
+
+ // clear() clears the cache of all elements.
+ inline void clear();
+
+ // Range based iterators.
+ inline iterator begin() const;
+ inline iterator end() const;
+
+private:
+ LRUCache(const LRUCache &) = delete;
+ LRUCache(LRUCache &&) = delete;
+ LRUCache &operator=(const LRUCache &) = delete;
+ LRUCache &operator=(LRUCache &&) = delete;
+
+ //Keyed holds a key. See find() for more information.
+ struct Keyed
+ {
+ Key key = {};
+ };
+
+ // Cache entry structure.
+ // Holds the unique copy of the key and data, along with pointers for
+ // maintaining the linked list.
+ struct Entry : Keyed
+ {
+ Data data = {};
+ Entry *next = nullptr;
+ Entry *prev = nullptr;
+ };
+
+ // KeyedComparator is a custom hasher and equality helper for Keyed.
+ struct KeyedComparator
+ {
+ // Hash function.
+ inline uint64_t operator()(const Keyed *k) const;
+
+ // Equality function.
+ inline uint64_t operator()(const Keyed *a, const Keyed *b) const;
+ };
+
+ // find() returns the Entry* for the given key, or nullptr.
+ // find() performs this by casting the Key pointer to a Keyed pointer for
+ // searching the std::unordered_set.
+ //
+ // This is to avoid having a duplicate Key held by both an
+ // unordered_map<Key, Entry*> and the Entry itself, as the Key type may be
+ // large.
+ //
+ // While we could use an unordered_set<Entry*>, this then requires the
+ // construction of a temporary Entry to perform the call to
+ // unordered_set<Entry*>::find(). This is undesirable as the Data type might
+ // be large or have an expensive constructor.
+ //
+ // C++20 gains a new templated overload for unordered_set<Entry*>::find()
+ // which would allow us to use unordered_set<Entry*>::find(Key*).
+ // Until we can use C++20, keep this casting nastiness hidden away in this
+ // one function.
+ inline Entry *find(const Key &key);
+
+ // unlinks the entry from the list it is linked in.
+ inline void unlink(Entry *);
+
+ // links the entry to the head of the LRU.
+ inline void link(Entry *);
+
+ // storage holds the allocations of all the entries.
+ // This vector must not change size for the lifetime of the cache.
+ std::vector<Entry> storage;
+
+ // set is an unordered set of Keyed*, using the KeyedComparator for hash and
+ // equality testing.
+ std::unordered_set<const Keyed *, KeyedComparator, KeyedComparator> set;
+
+ Entry *free = nullptr; // Singly-linked (prev is nullptr) list of free entries.
+ Entry *head = nullptr; // Pointer to the most recently used entry in the LRU.
+ Entry *tail = nullptr; // Pointer to the least recently used entry in the LRU.
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// LRUCache<>::view
+////////////////////////////////////////////////////////////////////////////////
+template<typename KEY, typename DATA, typename HASH>
+LRUCache<KEY, DATA, HASH>::view::view(Entry *entry)
+ : entry(entry)
+{}
+
+template<typename KEY, typename DATA, typename HASH>
+const KEY &LRUCache<KEY, DATA, HASH>::view::key() const
+{
+ return entry->key;
+}
+
+template<typename KEY, typename DATA, typename HASH>
+const DATA &LRUCache<KEY, DATA, HASH>::view::data() const
+{
+ return entry->data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// LRUCache<>::iterator
+////////////////////////////////////////////////////////////////////////////////
+template<typename KEY, typename DATA, typename HASH>
+LRUCache<KEY, DATA, HASH>::iterator::iterator(Entry *entry)
+ : entry(entry)
+{}
+
+template<typename KEY, typename DATA, typename HASH>
+typename LRUCache<KEY, DATA, HASH>::view LRUCache<KEY, DATA, HASH>::iterator::operator*() const
+{
+ return view{ entry };
+}
+
+template<typename KEY, typename DATA, typename HASH>
+typename LRUCache<KEY, DATA, HASH>::iterator &LRUCache<KEY, DATA, HASH>::iterator::operator++()
+{
+ entry = entry->next;
+ return *this;
+}
+
+template<typename KEY, typename DATA, typename HASH>
+bool LRUCache<KEY, DATA, HASH>::iterator::operator==(const iterator &rhs) const
+{
+ return entry == rhs.entry;
+}
+
+template<typename KEY, typename DATA, typename HASH>
+bool LRUCache<KEY, DATA, HASH>::iterator::operator!=(const iterator &rhs) const
+{
+ return entry != rhs.entry;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// LRUCache<>
+////////////////////////////////////////////////////////////////////////////////
+template<typename KEY, typename DATA, typename HASH>
+LRUCache<KEY, DATA, HASH>::LRUCache(size_t capacity)
+ : storage(capacity)
+{
+ for(size_t i = 0; i < capacity; i++)
+ {
+ Entry *entry = &storage[i];
+ entry->next = free; // No need for back link here.
+ free = entry;
+ }
+}
+
+template<typename KEY, typename DATA, typename HASH>
+DATA LRUCache<KEY, DATA, HASH>::lookup(const Key &key)
+{
+ if(Entry *entry = find(key))
+ {
+ unlink(entry);
+ link(entry);
+ return entry->data;
+ }
+ return {};
+}
+
+template<typename KEY, typename DATA, typename HASH>
+void LRUCache<KEY, DATA, HASH>::add(const Key &key, const Data &data)
+{
+ if(Entry *entry = find(key))
+ {
+ // Move entry to front.
+ unlink(entry);
+ link(entry);
+ entry->data = data;
+ return;
+ }
+
+ Entry *entry = free;
+ if(entry)
+ {
+ // Unlink from free.
+ free = entry->next;
+ entry->next = nullptr;
+ }
+ else
+ {
+ // Unlink least recently used.
+ entry = tail;
+ unlink(entry);
+ set.erase(entry);
+ }
+
+ // link as most recently used.
+ link(entry);
+ if(tail == nullptr)
+ {
+ tail = entry;
+ }
+
+ entry->key = key;
+ entry->data = data;
+ set.emplace(entry);
+}
+
+template<typename KEY, typename DATA, typename HASH>
+void LRUCache<KEY, DATA, HASH>::clear()
+{
+ while(Entry *entry = head)
+ {
+ unlink(entry);
+ entry->next = free; // No need for back link here.
+ free = entry;
+ }
+ set.clear();
+}
+
+template<typename KEY, typename DATA, typename HASH>
+typename LRUCache<KEY, DATA, HASH>::iterator LRUCache<KEY, DATA, HASH>::begin() const
+{
+ return { head };
+}
+
+template<typename KEY, typename DATA, typename HASH>
+typename LRUCache<KEY, DATA, HASH>::iterator LRUCache<KEY, DATA, HASH>::end() const
+{
+ return { nullptr };
+}
+
+template<typename KEY, typename DATA, typename HASH>
+void LRUCache<KEY, DATA, HASH>::unlink(Entry *entry)
+{
+ if(head == entry) { head = entry->next; }
+ if(tail == entry) { tail = entry->prev; }
+ if(entry->prev) { entry->prev->next = entry->next; }
+ if(entry->next) { entry->next->prev = entry->prev; }
+ entry->prev = nullptr;
+ entry->next = nullptr;
+}
+
+template<typename KEY, typename DATA, typename HASH>
+void LRUCache<KEY, DATA, HASH>::link(Entry *entry)
+{
+ ASSERT_MSG(entry->next == nullptr, "link() called on entry already linked");
+ ASSERT_MSG(entry->prev == nullptr, "link() called on entry already linked");
+ if(head)
+ {
+ entry->next = head;
+ head->prev = entry;
+ }
+ head = entry;
+ if(!tail) { tail = entry; }
+}
+
+template<typename KEY, typename DATA, typename HASH>
+typename LRUCache<KEY, DATA, HASH>::Entry *LRUCache<KEY, DATA, HASH>::find(const Key &key)
+{
+ auto asKeyed = reinterpret_cast<const Keyed *>(&key);
+ auto it = set.find(asKeyed);
+ if(it == set.end())
+ {
+ return nullptr;
+ }
+ return const_cast<Entry *>(static_cast<const Entry *>(*it));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// LRUCache<>::KeyedComparator
+////////////////////////////////////////////////////////////////////////////////
+template<typename KEY, typename DATA, typename HASH>
+uint64_t LRUCache<KEY, DATA, HASH>::KeyedComparator::operator()(const Keyed *k) const
+{
+ return Hash()(k->key);
+}
+
+template<typename KEY, typename DATA, typename HASH>
+uint64_t LRUCache<KEY, DATA, HASH>::KeyedComparator::operator()(const Keyed *a, const Keyed *b) const
+{
+ return a->key == b->key;
+}
+
+} // namespace sw
+
+#endif // sw_LRUCache_hpp
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index 982c10a..0961243 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -38,16 +38,21 @@
namespace vk {
-rr::Routine *Device::SamplingRoutineCache::querySnapshot(const vk::Device::SamplingRoutineCache::Key &key) const
-{
- return cache.querySnapshot(key).get();
-}
-
void Device::SamplingRoutineCache::updateSnapshot()
{
- std::lock_guard<std::mutex> lock(mutex);
+ std::unique_lock<std::mutex> lock(mutex);
- cache.updateSnapshot();
+ if(snapshotNeedsUpdate)
+ {
+ snapshot.clear();
+
+ for(auto it : cache)
+ {
+ snapshot[it.key()] = it.data();
+ }
+
+ snapshotNeedsUpdate = false;
+ }
}
Device::SamplerIndexer::~SamplerIndexer()
@@ -57,7 +62,7 @@
uint32_t Device::SamplerIndexer::index(const SamplerState &samplerState)
{
- std::lock_guard<std::mutex> lock(mutex);
+ std::unique_lock<std::mutex> lock(mutex);
auto it = map.find(samplerState);
@@ -76,7 +81,7 @@
void Device::SamplerIndexer::remove(const SamplerState &samplerState)
{
- std::lock_guard<std::mutex> lock(mutex);
+ std::unique_lock<std::mutex> lock(mutex);
auto it = map.find(samplerState);
ASSERT(it != map.end());
@@ -293,11 +298,6 @@
return samplingRoutineCache.get();
}
-rr::Routine *Device::querySnapshotCache(const SamplingRoutineCache::Key &key) const
-{
- return samplingRoutineCache->querySnapshot(key);
-}
-
void Device::updateSamplingRoutineSnapshotCache()
{
samplingRoutineCache->updateSnapshot();
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index 8931949..abf1191 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -17,12 +17,13 @@
#include "VkObject.hpp"
#include "VkSampler.hpp"
-#include "Device/LRUCache.hpp"
#include "Reactor/Routine.hpp"
+#include "System/LRUCache.hpp"
#include <map>
#include <memory>
#include <mutex>
+#include <unordered_map>
namespace marl {
class Scheduler;
@@ -95,29 +96,33 @@
template<typename Function>
std::shared_ptr<rr::Routine> getOrCreate(const Key &key, Function &&createRoutine)
{
- std::lock_guard<std::mutex> lock(mutex);
+ auto it = snapshot.find(key);
+ if(it != snapshot.end()) { return it->second; }
- if(auto existingRoutine = cache.query(key))
+ std::unique_lock<std::mutex> lock(mutex);
+ if(auto existingRoutine = cache.lookup(key))
{
return existingRoutine;
}
std::shared_ptr<rr::Routine> newRoutine = createRoutine(key);
cache.add(key, newRoutine);
+ snapshotNeedsUpdate = true;
return newRoutine;
}
- rr::Routine *querySnapshot(const Key &key) const;
void updateSnapshot();
private:
- sw::LRUSnapshotCache<Key, std::shared_ptr<rr::Routine>, Key::Hash> cache; // guarded by mutex
+ bool snapshotNeedsUpdate = false;
+ std::unordered_map<Key, std::shared_ptr<rr::Routine>, Key::Hash> snapshot;
+
+ sw::LRUCache<Key, std::shared_ptr<rr::Routine>, Key::Hash> cache; // guarded by mutex
std::mutex mutex;
};
SamplingRoutineCache *getSamplingRoutineCache() const;
- rr::Routine *querySnapshotCache(const SamplingRoutineCache::Key &key) const;
void updateSamplingRoutineSnapshotCache();
class SamplerIndexer