Clean up clutstore.* and add LRU cache
This commit adds a true LRU cache to `rtengine` which is used in the new `CLUTStore` class. The code in `clutstore.*` was cleaned up with C++11 features and small optimizations taken from my `clutbench` project. The `CLUTStore` class was converted to a true singleton.
This commit is contained in:
236
rtengine/cache.h
Normal file
236
rtengine/cache.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2016 Flössie <floessie.mail@gmail.com>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../rtgui/threadutils.h"
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
namespace cache_helper
|
||||
{
|
||||
|
||||
// See http://stackoverflow.com/a/20790050
|
||||
template<typename, typename = void>
|
||||
struct has_hash
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct has_hash<T, decltype(std::hash<T>()(std::declval<T>()), void())>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<class K, class V>
|
||||
class Cache
|
||||
{
|
||||
public:
|
||||
class Hook
|
||||
{
|
||||
public:
|
||||
virtual ~Hook()
|
||||
{
|
||||
}
|
||||
virtual void onDiscard(const K& key, const V& value) = 0;
|
||||
virtual void onDisplace(const K& key, const V& value) = 0;
|
||||
virtual void onRemove(const K& key, const V& value) = 0;
|
||||
virtual void onDestroy() = 0;
|
||||
};
|
||||
|
||||
Cache(unsigned long _size, Hook* _hook = 0) :
|
||||
store_size(_size),
|
||||
hook(_hook)
|
||||
{
|
||||
}
|
||||
|
||||
~Cache()
|
||||
{
|
||||
if (hook) {
|
||||
resize(0);
|
||||
hook->onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool get(const K& key, V& value) const
|
||||
{
|
||||
mutex.lock();
|
||||
const StoreConstIterator store_it = store.find(key);
|
||||
const bool present = store_it != store.end();
|
||||
if (present) {
|
||||
lru_list.splice(
|
||||
lru_list.begin(),
|
||||
lru_list,
|
||||
store_it->second.lru_list_it
|
||||
);
|
||||
value = store_it->second.value;
|
||||
}
|
||||
mutex.unlock();
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
bool set(const K& key, const V& value)
|
||||
{
|
||||
return set(key, value, Mode::UNCOND);
|
||||
}
|
||||
|
||||
bool replace(const K& key, const V& value)
|
||||
{
|
||||
return set(key, value, Mode::KNOWN);
|
||||
}
|
||||
|
||||
bool insert(const K& key, const V& value)
|
||||
{
|
||||
return set(key, value, Mode::UNKNOWN);
|
||||
}
|
||||
|
||||
bool remove(const K& key)
|
||||
{
|
||||
mutex.lock();
|
||||
const StoreIterator store_it = store.find(key);
|
||||
const bool present = store_it != store.end();
|
||||
if (present) {
|
||||
remove(store_it);
|
||||
}
|
||||
mutex.unlock();
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
void resize(unsigned long size)
|
||||
{
|
||||
mutex.lock();
|
||||
while (lru_list.size() > size) {
|
||||
discard();
|
||||
}
|
||||
store_size = size;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
mutex.lock();
|
||||
if (hook) {
|
||||
for (const auto& entry : store) {
|
||||
hook->onRemove(entry.first, entry.second.value);
|
||||
}
|
||||
}
|
||||
lru_list.clear();
|
||||
store.clear();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Value;
|
||||
|
||||
using Store = typename std::conditional<
|
||||
cache_helper::has_hash<K>::value,
|
||||
std::unordered_map<K, Value>,
|
||||
std::map<K, Value>
|
||||
>::type;
|
||||
using StoreIterator = typename Store::iterator;
|
||||
using StoreConstIterator = typename Store::const_iterator;
|
||||
|
||||
typedef std::list<StoreIterator> LruList;
|
||||
using LruListIterator = typename LruList::iterator;
|
||||
|
||||
struct Value {
|
||||
V value;
|
||||
LruListIterator lru_list_it;
|
||||
};
|
||||
|
||||
enum class Mode {
|
||||
UNCOND,
|
||||
KNOWN,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
void discard()
|
||||
{
|
||||
const StoreIterator store_it = lru_list.back();
|
||||
if (hook) {
|
||||
hook->onDiscard(store_it->first, store_it->second.value);
|
||||
}
|
||||
store.erase(store_it);
|
||||
lru_list.pop_back();
|
||||
}
|
||||
|
||||
bool set(const K& key, const V& value, Mode mode)
|
||||
{
|
||||
mutex.lock();
|
||||
const StoreIterator store_it = store.find(key);
|
||||
const bool is_new_key = store_it == store.end();
|
||||
if (is_new_key) {
|
||||
if (mode == Mode::UNCOND || mode == Mode::UNKNOWN) {
|
||||
if (lru_list.size() >= store_size) {
|
||||
discard();
|
||||
}
|
||||
lru_list.push_front(store.end());
|
||||
const Value v = {
|
||||
value,
|
||||
lru_list.begin()
|
||||
};
|
||||
lru_list.front() = store.emplace(key, v).first;
|
||||
}
|
||||
} else {
|
||||
if (mode == Mode::UNCOND || mode == Mode::KNOWN) {
|
||||
if (hook) {
|
||||
hook->onDisplace(key, store_it->second.value);
|
||||
}
|
||||
lru_list.splice(
|
||||
lru_list.begin(),
|
||||
lru_list,
|
||||
store_it->second.lru_list_it
|
||||
);
|
||||
store_it->second.value = value;
|
||||
}
|
||||
}
|
||||
mutex.unlock();
|
||||
|
||||
return is_new_key;
|
||||
}
|
||||
|
||||
void remove(const StoreIterator& store_it)
|
||||
{
|
||||
if (hook) {
|
||||
hook->onRemove(store_it->first, store_it->second.value);
|
||||
}
|
||||
lru_list.erase(store_it->second.lru_list_it);
|
||||
store.erase(store_it);
|
||||
}
|
||||
|
||||
unsigned long store_size;
|
||||
Hook* const hook;
|
||||
mutable MyMutex mutex;
|
||||
Store store;
|
||||
mutable LruList lru_list;
|
||||
};
|
||||
|
||||
}
|
Reference in New Issue
Block a user