From 18c95e6799522a86c20d2c50fddecea27cf18a87 Mon Sep 17 00:00:00 2001 From: Simon Praetorius <simon.praetorius@tu-dresden.de> Date: Thu, 19 Jul 2018 14:54:32 +0200 Subject: [PATCH] cache with concurrent access policies added --- src/amdis/common/TupleUtility.hpp | 26 ++++++++ src/amdis/utility/ConcurrentCache.hpp | 86 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/amdis/utility/ConcurrentCache.hpp diff --git a/src/amdis/common/TupleUtility.hpp b/src/amdis/common/TupleUtility.hpp index 0a6f1c03..8b10ec3f 100644 --- a/src/amdis/common/TupleUtility.hpp +++ b/src/amdis/common/TupleUtility.hpp @@ -3,12 +3,38 @@ #include <tuple> #include <type_traits> +#include <dune/common/hash.hh> + #include <amdis/common/IndexSeq.hpp> #include <amdis/common/Mpl.hpp> #include <amdis/common/Utility.hpp> namespace AMDiS { + namespace Impl + { + // Recursive template code derived from Matthieu M. + template <class Tuple, std::size_t I = std::tuple_size<Tuple>::value - 1> + struct HashTupleImpl + { + static void apply(size_t& seed, Tuple const& tuple) + { + HashTupleImpl<Tuple, I-1>::apply(seed, tuple); + Dune::hash_combine(seed, std::get<I>(tuple)); + } + }; + + template <class Tuple> + struct HashTupleImpl<Tuple, 0> + { + static void apply(std::size_t& seed, Tuple const& tuple) + { + Dune::hash_combine(seed, std::get<0>(tuple)); + } + }; + + } // end namespace Impl + namespace Impl { template <class Tuple, std::size_t N> diff --git a/src/amdis/utility/ConcurrentCache.hpp b/src/amdis/utility/ConcurrentCache.hpp new file mode 100644 index 00000000..884a1360 --- /dev/null +++ b/src/amdis/utility/ConcurrentCache.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include <mutex> +#include <thread> +#include <tuple> +#include <unordered_map> + +#include <amdis/common/Concepts.hpp> + +namespace AMDiS +{ + namespace tag + { + struct thread_local_policy {}; + struct shared_policy {}; + } + + + template <class Key, + class Data, + class Policy = tag::thread_local_policy> + class ConcurrentCache + { + using key_type = Key; + using data_type = Data; + + public: + + /// Return the data associated to the key. If not yet initialized, call functor f + template <class F, + REQUIRES(Concepts::Callable<F,data_type*,key_type>)> + static data_type const& get(key_type key, F&& f) + { + return instance(Policy{}).get_or_init(key, std::forward<F>(f)); + } + + private: + + auto& guarded_get(key_type const& key, tag::shared_policy) + { + std::lock_guard<std::mutex> lock(access_mutex); + return cached_data[key]; + } + + auto& guarded_get(key_type const& key, tag::thread_local_policy) + { + return cached_data[key]; + } + + template <class F> + data_type const& get_or_init(key_type const& key, F&& f) + { + auto& data = guarded_get(key, Policy{}); + + std::call_once(data.first, std::forward<F>(f), &data.second, key); + return data.second; + } + + private: + // Return an instance of this class with static storage + static ConcurrentCache& instance(tag::shared_policy) + { + static ConcurrentCache cache; + return cache; + } + + // Return an instance of this class with thread_local storage + static ConcurrentCache& instance(tag::thread_local_policy) + { + thread_local ConcurrentCache cache; + return cache; + } + + // private constructor + ConcurrentCache() = default; + + // mutex used to access the data in the container, necessary since + // access by operator[] is read+write access, i.e. an empty data element + // is created if it does not exist yet. + std::mutex access_mutex; + + // Container to store the cached values + std::unordered_map<key_type, std::pair<std::once_flag, data_type>> cached_data; + }; + +} // end namespace AMDiS -- GitLab