JKQTPlotter trunk/v5.0.0
an extensive Qt5+Qt6 Plotter framework (including a feature-richt plotter widget, a speed-optimized, but limited variant and a LaTeX equation renderer!), written fully in C/C++ and without external dependencies
Loading...
Searching...
No Matches
jkqtpcachingtools.h
1/*
2 Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
3
4 last modification: $LastChangedDate$ (revision $Rev$)
5
6 This software is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License (LGPL) as published by
8 the Free Software Foundation, either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License (LGPL) for more details.
15
16 You should have received a copy of the GNU Lesser General Public License (LGPL)
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20
21
22
23
24#ifndef JKQTPCACHINGTOOLS_H
25#define JKQTPCACHINGTOOLS_H
26
27#include "jkqtcommon/jkqtcommon_imexport.h"
28#include "jkqtcommon/jkqtpmathtools.h"
29#include <QReadWriteLock>
30#include <QReadLocker>
31#include <QWriteLocker>
32#include <functional>
33#include <chrono>
34#include <atomic>
35#include <algorithm>
36#include <memory>
37#include <vector>
38#include <unordered_map>
39
40/** \brief tag type to configure JKQTPDataCache for thread-safety
41 * \ingroup jkqtptools_concurrency
42 */
44/** \brief tag type to configure JKQTPDataCache for non thread-safety
45 * \ingroup jkqtptools_concurrency
46 */
48
49/** \brief this class can be used to implement a general cache for values
50 * \ingroup jkqtptools_concurrency
51 *
52 * It is typically used to generate a static (ThreadSafe=true) of thread_local (ThreadSafe=false) cache inside a function.
53 *
54 * The class is parametrized by a key (TKeay) and value (TData) data type and receives (in the constructor) a functor that
55 * calculates the data for a key. An additional template parameter ThreadSafe indicates (true|false) whether the class
56 * is implemented in a thread-safe way (for static instances) or not (then it should be used as thread_local instances in a
57 * multi-threaded environment or in a single-thread environment).
58 *
59 * The class member function get(key) returns a value for a given key, which is either taken from the internal cache,
60 * or generated using the functor provided to the constructor. In the latter case the generated value is stored in the internal cache.
61 *
62 * Internally the cache maps TKey to TData, but the signature of the get()-function and the generator functor actually uses TKeyInSignature,
63 * which may differ from TKey. The only limitation is that TKeyInSignature can be converted/assigned to a TKey
64 *
65 * The cache has a maximmum size m_maxEntries.
66 * When you try to add a new object, after which the size would grow beyond this, a fraction 1-m_retainFraction of elements are
67 * deleted from the cache. The delete strategy is least-recently used (LRU). In order to immplement this, the cache keeps track of
68 * the last use timestamp of each entry.
69 *
70 * You can deactivate the cleaning by setting m_maxEntries<0, but the the cache may grow indefinitely and there is possibly undefined behaviour
71 * when add one too many items!
72 */
73template <class TData, class TKey, typename ThreadSafe=JKQTPDataCacheThreadSafe, class TKeyInSignature=TKey>
75 template <typename FF>
76 inline JKQTPDataCache(FF generateData, int maxEntries=10000, double retainFraction=0.8):
77 m_maxEntries(maxEntries), m_retainFraction(retainFraction), m_generateData(std::forward<FF>(generateData))
78 {
79
80 }
86
87 template<class... Args>
88 inline TData get_inline(Args... args) {
89 return get(TKeyInSignature(args...));
90 }
91
92 template <typename TSS=ThreadSafe>
93 inline TData get(const typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, TKeyInSignature>::type& key) {
94 const TKey cacheKey=key;
95
96 QReadLocker lockR(&m_mutex);
97 auto it=m_cache.find(cacheKey);
98 if (m_cache.end()!=it) {
99 m_cacheLastUseTimestamps[cacheKey]->exchange(currenTimestamp());
100 return it->second;
101 }
102 lockR.unlock();
103
104 QWriteLocker lockW(&m_mutex);
105 it=m_cache.find(cacheKey);
106 if (m_cache.end()!=it) {
107 m_cacheLastUseTimestamps.at(cacheKey)->exchange(currenTimestamp());
108 return it->second;
109 }
110 if (m_maxEntries>0 && m_cache.size()>=static_cast<size_t>(m_maxEntries)) cleanCache_notThreadSafe();
111 m_cacheLastUseTimestamps.emplace(cacheKey, std::make_shared<std::atomic<int64_t> >(currenTimestamp()));
112 const auto newData=m_generateData(key);
113 m_cache.emplace(cacheKey,newData);
114 return newData;
115 }
116
117 template <typename TSS=ThreadSafe>
118 inline TData get(const typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, TKeyInSignature>::type& key) {
119 const TKey cacheKey=key;
120
121 auto it=m_cache.find(cacheKey);
122 if (m_cache.end()!=it) {
123 m_cacheLastUseTimestamps[cacheKey]->exchange(currenTimestamp());
124 return it->second;
125 }
126 if (m_maxEntries>0 && m_cache.size()>=static_cast<size_t>(m_maxEntries)) cleanCache_notThreadSafe();
127 const auto newData=m_generateData(key);
128 m_cache.emplace(cacheKey,newData);
129 m_cacheLastUseTimestamps.emplace(cacheKey, std::make_shared<std::atomic<int64_t> >(currenTimestamp()));
130 return newData;
131 }
132
133 template <typename TSS=ThreadSafe>
134 inline bool contains(const typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, TKeyInSignature>::type& key) const {
135 const TKey cacheKey=key;
136
137 QReadLocker lockR(&m_mutex);
138 return m_cache.find(cacheKey)!=m_cache.end();
139 }
140
141 template <typename TSS=ThreadSafe>
142 inline bool contains(const typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, TKeyInSignature>::type& key) const {
143 const TKey cacheKey=key;
144 return m_cache.find(cacheKey)!=m_cache.end();
145 }
146
147
148 inline int size() const {
149 return size_impl<ThreadSafe>();
150 }
151
152private:
153 template <typename TSS>
154 inline typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, int>::type size_impl() const {
156 return m_cache.size();
157 }
158
159 template <typename TSS>
160 inline typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, int>::type size_impl() const {
161 return m_cache.size();
162 }
163
164 /** \brief generate a timestamp */
165 static inline int64_t currenTimestamp() {
166 static auto firstTime=std::chrono::steady_clock::now();
167 return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-firstTime).count();
168 }
169 /** \brief clean the cache, so at m_retainFraction*m_maxEntries entries remain. */
171 if (m_maxEntries<0 || m_cache.size()<static_cast<size_t>(m_maxEntries)) return;
172 const int deleteItems=jkqtp_boundedRoundTo<int>(1, (1.0-m_retainFraction)*static_cast<double>(m_cache.size()), m_cache.size());
173 std::vector<QPair<TKey,int64_t> > allItems;
174 allItems.reserve(m_cacheLastUseTimestamps.size());
175 for (auto it=m_cacheLastUseTimestamps.begin(); it!=m_cacheLastUseTimestamps.end(); ++it) {
176 allItems.emplace_back(it->first, it->second->load());
177 }
178 std::sort(allItems.begin(), allItems.end(), [](const QPair<TKey,int64_t>&a, const QPair<TKey,int64_t>&b) {return a.second>b.second;});
179 for (int i=0; i<deleteItems; i++) {
180 m_cache.erase(allItems[i].first);
182 }
183 }
184 const int m_maxEntries;
185 const double m_retainFraction;
186 std::unordered_map<TKey, TData> m_cache;
187 std::unordered_map<TKey, std::shared_ptr<std::atomic<int64_t> > > m_cacheLastUseTimestamps;
189 const std::function<TData(TKeyInSignature)> m_generateData;
190};
191
192#endif // JKQTPCACHINGTOOLS_H
tag type to configure JKQTPDataCache for non thread-safety
Definition jkqtpcachingtools.h:47
tag type to configure JKQTPDataCache for thread-safety
Definition jkqtpcachingtools.h:43
this class can be used to implement a general cache for values
Definition jkqtpcachingtools.h:74
JKQTPDataCache & operator=(const JKQTPDataCache &)=delete
int size() const
Definition jkqtpcachingtools.h:148
JKQTPDataCache(FF generateData, int maxEntries=10000, double retainFraction=0.8)
Definition jkqtpcachingtools.h:76
std::unordered_map< TKey, std::shared_ptr< std::atomic< int64_t > > > m_cacheLastUseTimestamps
Definition jkqtpcachingtools.h:187
const std::function< TData(TKeyInSignature)> m_generateData
Definition jkqtpcachingtools.h:189
QReadWriteLock m_mutex
Definition jkqtpcachingtools.h:188
TData get(const typename std::enable_if< std::is_same< JKQTPDataCacheNotThreadSafe, TSS >::value, TKeyInSignature >::type &key)
Definition jkqtpcachingtools.h:118
const double m_retainFraction
Definition jkqtpcachingtools.h:185
const int m_maxEntries
Definition jkqtpcachingtools.h:184
JKQTPDataCache & operator=(JKQTPDataCache &&)=default
JKQTPDataCache(JKQTPDataCache &&)=default
std::enable_if< std::is_same< JKQTPDataCacheThreadSafe, TSS >::value, int >::type size_impl() const
Definition jkqtpcachingtools.h:154
static int64_t currenTimestamp()
generate a timestamp
Definition jkqtpcachingtools.h:165
bool contains(const typename std::enable_if< std::is_same< JKQTPDataCacheThreadSafe, TSS >::value, TKeyInSignature >::type &key) const
Definition jkqtpcachingtools.h:134
TData get_inline(Args... args)
Definition jkqtpcachingtools.h:88
JKQTPDataCache(const JKQTPDataCache &)=delete
TData get(const typename std::enable_if< std::is_same< JKQTPDataCacheThreadSafe, TSS >::value, TKeyInSignature >::type &key)
Definition jkqtpcachingtools.h:93
void cleanCache_notThreadSafe()
clean the cache, so at m_retainFraction*m_maxEntries entries remain.
Definition jkqtpcachingtools.h:170
std::enable_if< std::is_same< JKQTPDataCacheNotThreadSafe, TSS >::value, int >::type size_impl() const
Definition jkqtpcachingtools.h:160
bool contains(const typename std::enable_if< std::is_same< JKQTPDataCacheNotThreadSafe, TSS >::value, TKeyInSignature >::type &key) const
Definition jkqtpcachingtools.h:142
JKQTPDataCache()=delete
std::unordered_map< TKey, TData > m_cache
Definition jkqtpcachingtools.h:186