Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcache3q_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QCACHE3Q_H
5#define QCACHE3Q_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16
17#include <QtCore/QSharedPointer>
18#include <QtCore/QDebug>
19
21
22template <class Key, class T>
24{
25protected:
26 /* called just before a key/value pair is about to be _evicted_ */
27 void aboutToBeEvicted(const Key &key, QSharedPointer<T> obj);
28 /* called just before a key/value pair is about to be removed, by
29 * clear(), remove() or by the destructor (which calls clear) */
30 void aboutToBeRemoved(const Key &key, QSharedPointer<T> obj);
31};
32
33template <class Key, class T>
39
40template <class Key, class T>
46
47/*
48 * QCache3Q
49 *
50 * A cache template class for managing QSharedPointers to objects with an
51 * associated key. It's a lot like QCache, but uses an alternative algorithm
52 * '3Q' -- which is the '2Q' algorithm plus an extra queue for previously popular
53 * but evicted nodes, and a 'ghost' list of recent evictions to make a better
54 * placement choice if they are requested again.
55 *
56 * New nodes enter the cache on the "newbies" queue, which is evicted LRA
57 * (least-recently-added). If a newbie is popular enough (it has been requested
58 * more than promoteAt times), it will be promoted to a "regular". Regulars
59 * are evicted LRU (least-recently-used). If a regular is under consideration
60 * for eviction, its popularity is compared to the mean popularity of the whole
61 * regulars queue. If it is greater, it is instead moved to the "hobos" queue.
62 * The "hobos" queue is also evicted LRU, but has a maximum size constraint
63 * so eviction from it is less likely than from the regulars.
64 *
65 * Tweakables:
66 * * maxCost = maximum total cost for the whole cache
67 * * minRecent = minimum size that q1 ("newbies") has to be before eviction
68 * from it takes place
69 * * maxOldPopular = maximum size that q3 ("hobos") can reach before eviction
70 * from it takes place
71 * * promoteAt = minimum popularity necessary to promote a node from
72 * "newbie" to "regular"
73 */
74template <class Key, class T, class EvPolicy = QCache3QDefaultEvictionPolicy<Key,T> >
75class QCache3Q : public EvPolicy
76{
77private:
78 class Queue;
79 class Node
80 {
81 public:
82 inline explicit Node() : q(0), n(0), p(0), pop(0), cost(0) {}
83
84 Queue *q;
85 Node *n;
86 Node *p;
87 Key k;
88 QSharedPointer<T> v;
89 quint64 pop; // popularity, incremented each ping
90 int cost;
91 };
92
93 class Queue
94 {
95 public:
96 inline explicit Queue() : f(0), l(0), cost(0), pop(0), size(0) {}
97
98 Node *f;
99 Node *l;
100 int cost; // total cost of nodes on the queue
101 quint64 pop; // sum of popularity values on the queue
102 int size; // size of the queue
103 };
104
105 Queue *q1_; // "newbies": seen only once, evicted LRA (least-recently-added)
106 Queue *q2_; // regular nodes, promoted from newbies, evicted LRU
107 Queue *q3_; // "hobos": evicted from q2 but were very popular (above mean)
108 Queue *q1_evicted_; // ghosts of recently evicted newbies and regulars
109 QHash<Key, Node *> lookup_;
110
111public:
112 explicit QCache3Q(int maxCost = 0, int minRecent = -1, int maxOldPopular = -1);
113 inline ~QCache3Q() { clear(); delete q1_; delete q2_; delete q3_; delete q1_evicted_; }
114
115 inline int maxCost() const { return maxCost_; }
116 void setMaxCost(int maxCost, int minRecent = -1, int maxOldPopular = -1);
117
118 inline int promoteAt() const { return promote_; }
119 inline void setPromoteAt(int p) { promote_ = p; }
120
121 inline int totalCost() const { return q1_->cost + q2_->cost + q3_->cost; }
122
123 void clear();
124 bool insert(const Key &key, QSharedPointer<T> object, int cost = 1);
125 QSharedPointer<T> object(const Key &key) const;
126 QSharedPointer<T> operator[](const Key &key) const;
127
128 void remove(const Key &key, bool force = false);
129 QList<Key> keys() const;
131
132 // Copy data directly into a queue. Designed for single use after construction
133 void deserializeQueue(int queueNumber, const QList<Key> &keys,
134 const QList<QSharedPointer<T> > &values, const QList<int> &costs);
135 // Copy data from specific queue into list
136 void serializeQueue(int queueNumber, QList<QSharedPointer<T> > &buffer);
137
138private:
139 int maxCost_, minRecent_, maxOldPopular_;
140 int hitCount_, missCount_, promote_;
141
142 void rebalance();
143 void unlink(Node *n);
144 void link_front(Node *n, Queue *q);
145
146private:
147 // make these private so they can't be used
148 inline QCache3Q(const QCache3Q<Key,T,EvPolicy> &) {}
149 inline QCache3Q<Key,T,EvPolicy> &operator=(const QCache3Q<Key,T,EvPolicy> &) {}
150};
151
152template <class Key, class T, class EvPolicy>
154{
155 qDebug("\n=== cache %p ===", this);
156 qDebug("hits: %d (%.2f%%)\tmisses: %d\tfill: %.2f%%", hitCount_,
157 100.0 * float(hitCount_) / (float(hitCount_ + missCount_)),
158 missCount_,
159 100.0 * float(totalCost()) / float(maxCost()));
160 qDebug("q1g: size=%d, pop=%llu", q1_evicted_->size, q1_evicted_->pop);
161 qDebug("q1: cost=%d, size=%d, pop=%llu", q1_->cost, q1_->size, q1_->pop);
162 qDebug("q2: cost=%d, size=%d, pop=%llu", q2_->cost, q2_->size, q2_->pop);
163 qDebug("q3: cost=%d, size=%d, pop=%llu", q3_->cost, q3_->size, q3_->pop);
164}
165
166template <class Key, class T, class EvPolicy>
167QCache3Q<Key,T,EvPolicy>::QCache3Q(int maxCost, int minRecent, int maxOldPopular)
168 : q1_(new Queue), q2_(new Queue), q3_(new Queue), q1_evicted_(new Queue),
169 maxCost_(maxCost), minRecent_(minRecent), maxOldPopular_(maxOldPopular),
170 hitCount_(0), missCount_(0), promote_(0)
171{
172 if (minRecent_ < 0)
173 minRecent_ = maxCost_ / 3;
174 if (maxOldPopular_ < 0)
175 maxOldPopular_ = maxCost_ / 5;
176}
177
178template <class Key, class T, class EvPolicy>
179void QCache3Q<Key,T,EvPolicy>::serializeQueue(int queueNumber, QList<QSharedPointer<T> > &buffer)
180{
181 Q_ASSERT(queueNumber >= 1 && queueNumber <= 4);
182 Queue *queue = queueNumber == 1 ? q1_ :
183 queueNumber == 2 ? q2_ :
184 queueNumber == 3 ? q3_ :
185 q1_evicted_;
186 for (Node *node = queue->f; node; node = node->n)
187 buffer.append(node->v);
188}
189
190template <class Key, class T, class EvPolicy>
191void QCache3Q<Key,T,EvPolicy>::deserializeQueue(int queueNumber, const QList<Key> &keys,
192 const QList<QSharedPointer<T> > &values, const QList<int> &costs)
193{
194 Q_ASSERT(queueNumber >= 1 && queueNumber <= 4);
195 int bufferSize = keys.size();
196 if (bufferSize == 0)
197 return;
198 clear();
199 Queue *queue = queueNumber == 1 ? q1_ :
200 queueNumber == 2 ? q2_ :
201 queueNumber == 3 ? q3_ :
202 q1_evicted_;
203 for (int i = 0; i<bufferSize; ++i) {
204 Node *node = new Node;
205 node->v = values[i];
206 node->k = keys[i];
207 node->cost = costs[i];
208 link_front(node, queue);
209 lookup_[keys[i]] = node;
210 }
211}
212
213
214template <class Key, class T, class EvPolicy>
215inline void QCache3Q<Key,T,EvPolicy>::setMaxCost(int maxCost, int minRecent, int maxOldPopular)
216{
217 maxCost_ = maxCost;
218 minRecent_ = minRecent;
219 maxOldPopular_ = maxOldPopular;
220 if (minRecent_ < 0)
221 minRecent_ = maxCost_ / 3;
222 if (maxOldPopular_ < 0)
223 maxOldPopular_ = maxCost_ / 5;
224 rebalance();
225}
226
227template <class Key, class T, class EvPolicy>
228bool QCache3Q<Key,T,EvPolicy>::insert(const Key &key, QSharedPointer<T> object, int cost)
229{
230 if (cost > maxCost_) {
231 return false;
232 }
233
234 if (lookup_.contains(key)) {
235 Node *n = lookup_[key];
236 n->v = object;
237 n->q->cost -= n->cost;
238 n->cost = cost;
239 n->q->cost += cost;
240
241 if (n->q == q1_evicted_) {
242 if (n->pop > (uint)promote_) {
243 unlink(n);
244 link_front(n, q2_);
245 rebalance();
246 }
247 } else if (n->q != q1_) {
248 Queue *q = n->q;
249 unlink(n);
250 link_front(n, q);
251 rebalance();
252 }
253
254 return true;
255 }
256
257 Node *n = new Node;
258 n->v = object;
259 n->k = key;
260 n->cost = cost;
261 link_front(n, q1_);
262 lookup_[key] = n;
263
264 rebalance();
265
266 return true;
267}
268
269template <class Key, class T, class EvPolicy>
271{
272 while (q1_evicted_->f) {
273 Node *n = q1_evicted_->f;
274 unlink(n);
275 delete n;
276 }
277
278 while (q1_->f) {
279 Node *n = q1_->f;
280 unlink(n);
281 EvPolicy::aboutToBeRemoved(n->k, n->v);
282 delete n;
283 }
284
285 while (q2_->f) {
286 Node *n = q2_->f;
287 unlink(n);
288 EvPolicy::aboutToBeRemoved(n->k, n->v);
289 delete n;
290 }
291
292 while (q3_->f) {
293 Node *n = q3_->f;
294 unlink(n);
295 EvPolicy::aboutToBeRemoved(n->k, n->v);
296 delete n;
297 }
298
299 lookup_.clear();
300}
301
302template <class Key, class T, class EvPolicy>
304{
305 if (n->n)
306 n->n->p = n->p;
307 if (n->p)
308 n->p->n = n->n;
309 if (n->q->f == n)
310 n->q->f = n->n;
311 if (n->q->l == n)
312 n->q->l = n->p;
313 n->n = 0;
314 n->p = 0;
315 n->q->pop -= n->pop;
316 n->q->cost -= n->cost;
317 n->q->size--;
318 n->q = 0;
319}
320
321template <class Key, class T, class EvPolicy>
323{
324 n->n = q->f;
325 n->p = 0;
326 n->q = q;
327 if (q->f)
328 q->f->p = n;
329 q->f = n;
330 if (!q->l)
331 q->l = n;
332
333 q->pop += n->pop;
334 q->cost += n->cost;
335 q->size++;
336}
337
338template <class Key, class T, class EvPolicy>
340{
341 while (q1_evicted_->size > (q1_->size + q2_->size + q3_->size) * 4) {
342 Node *n = q1_evicted_->l;
343 unlink(n);
344 lookup_.remove(n->k);
345 delete n;
346 }
347
348 while ((q1_->cost + q2_->cost + q3_->cost) > maxCost_) {
349 if (q3_->cost > maxOldPopular_) {
350 Node *n = q3_->l;
351 unlink(n);
352 EvPolicy::aboutToBeEvicted(n->k, n->v);
353 lookup_.remove(n->k);
354 delete n;
355 } else if (q1_->cost > minRecent_) {
356 Node *n = q1_->l;
357 unlink(n);
358 EvPolicy::aboutToBeEvicted(n->k, n->v);
359 n->v.clear();
360 n->cost = 0;
361 link_front(n, q1_evicted_);
362 } else {
363 Node *n = q2_->l;
364 unlink(n);
365 if (q2_->size && n->pop > (q2_->pop / q2_->size)) {
366 link_front(n, q3_);
367 } else {
368 EvPolicy::aboutToBeEvicted(n->k, n->v);
369 n->v.clear();
370 n->cost = 0;
371 link_front(n, q1_evicted_);
372 }
373 }
374 }
375}
376
377template <class Key, class T, class EvPolicy>
379{
380 if (!lookup_.contains(key)) {
381 return;
382 }
383 Node *n = lookup_[key];
384 unlink(n);
385 if (n->q != q1_evicted_ && !force)
386 EvPolicy::aboutToBeRemoved(n->k, n->v);
387 lookup_.remove(key);
388 delete n;
389}
390
391template <class Key, class T, class EvPolicy>
393{
394 return lookup_.keys();
395}
396
397template <class Key, class T, class EvPolicy>
398QSharedPointer<T> QCache3Q<Key,T,EvPolicy>::object(const Key &key) const
399{
400 if (!lookup_.contains(key)) {
401 const_cast<QCache3Q<Key,T,EvPolicy> *>(this)->missCount_++;
402 return QSharedPointer<T>();
403 }
404
405 QCache3Q<Key,T,EvPolicy> *me = const_cast<QCache3Q<Key,T,EvPolicy> *>(this);
406
407 Node *n = me->lookup_[key];
408 n->pop++;
409 n->q->pop++;
410
411 if (n->q == q1_) {
412 me->hitCount_++;
413
414 if (n->pop > (quint64)promote_) {
415 me->unlink(n);
416 me->link_front(n, q2_);
417 me->rebalance();
418 }
419 } else if (n->q != q1_evicted_) {
420 me->hitCount_++;
421
422 Queue *q = n->q;
423 me->unlink(n);
424 me->link_front(n, q);
425 me->rebalance();
426 } else {
427 me->missCount_++;
428 }
429
430 return n->v;
431}
432
433template <class Key, class T, class EvPolicy>
434inline QSharedPointer<T> QCache3Q<Key,T,EvPolicy>::operator[](const Key &key) const
435{
436 return object(key);
437}
438
440
441#endif // QCACHE3Q_H
Definition lalr.h:136
void aboutToBeEvicted(const Key &key, QSharedPointer< T > obj)
Definition qcache3q_p.h:34
void aboutToBeRemoved(const Key &key, QSharedPointer< T > obj)
Definition qcache3q_p.h:41
int totalCost() const
Definition qcache3q_p.h:121
void clear()
Definition qcache3q_p.h:270
QSharedPointer< T > operator[](const Key &key) const
Definition qcache3q_p.h:434
int maxCost() const
Definition qcache3q_p.h:115
QCache3Q(int maxCost=0, int minRecent=-1, int maxOldPopular=-1)
Definition qcache3q_p.h:167
void deserializeQueue(int queueNumber, const QList< Key > &keys, const QList< QSharedPointer< T > > &values, const QList< int > &costs)
Definition qcache3q_p.h:191
bool insert(const Key &key, QSharedPointer< T > object, int cost=1)
Definition qcache3q_p.h:228
void remove(const Key &key, bool force=false)
Definition qcache3q_p.h:378
QSharedPointer< T > object(const Key &key) const
Definition qcache3q_p.h:398
void setMaxCost(int maxCost, int minRecent=-1, int maxOldPopular=-1)
Definition qcache3q_p.h:215
void printStats()
Definition qcache3q_p.h:153
int promoteAt() const
Definition qcache3q_p.h:118
QList< Key > keys() const
Definition qcache3q_p.h:392
void setPromoteAt(int p)
Definition qcache3q_p.h:119
void serializeQueue(int queueNumber, QList< QSharedPointer< T > > &buffer)
Definition qcache3q_p.h:179
Definition qlist.h:75
b clear()
Combined button and popup list for selecting options.
#define qDebug
[1]
Definition qlogging.h:164
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei const GLfloat * v
[13]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint object
[3]
GLfloat GLfloat f
GLenum GLuint buffer
GLfloat n
GLhandleARB obj
[2]
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
static qsizetype cost(const QPixmap &pixmap)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
unsigned int uint
Definition qtypes.h:34
QStringList keys
QQueue< int > queue
[0]