LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ldes_cache.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_LDES_LDES_CACHE_H
6#define LINE_SOLVERS_LDES_LDES_CACHE_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The Cache node of the native LDES engine.
12 *
13 * A cache holds items in ORDERED LISTS, not one flat set. List l has its own
14 * capacity, an item hit in list l may be PROMOTED to a higher list, and the
15 * displaced occupant is DEMOTED back down. That structure is what distinguishes
16 * the segmented policies (HLRU, CLIMB, SFIFO) from the flat ones (LRU, FIFO,
17 * RR, QLRU), which are the same machinery with a single list.
18 *
19 * THE HIT IS AN EXACT SAMPLE-PATH EVENT, not a probability. The engine holds
20 * the real list contents and looks the item up, so a hit is a hit; the
21 * analytical solvers estimate a hit RATE instead. That is the whole reason a
22 * simulator is asked about a cache at all.
23 *
24 * A COST CAP IS SERVED, NOT REFUSED. When an insertion or a promotion would
25 * push a list past its storage-cost cap, the request is SERVED and the cache
26 * state is left unchanged -- the "serve but do not swap" rule of RR-C(m)
27 * (Casale-Gast, IEEE/ACM ToN 29(2), 2021, Sec. IX). Refusing the request
28 * instead would drop a job the model never loses.
29 */
30
31#include <algorithm>
32#include <cstddef>
33#include <list>
34#include <string>
35#include <vector>
36
38
39namespace line {
40namespace ldes {
41namespace engine {
42
43/** Live state of one Cache node. */
44struct CacheState {
45 std::size_t node = 0;
46 std::size_t nitems = 0;
48 double qlru = 1.0; ///< q-LRU admission probability on a miss
49 std::vector<std::size_t> capacity; ///< per list
50 std::vector<std::list<std::size_t>> lists; ///< per list, most recent at the front
51 std::vector<double> item_size; ///< empty = unconstrained
52 std::vector<double> cost_cap; ///< empty = unconstrained
53 /** Per access class: the item popularity, as a cumulative law. */
54 std::vector<std::vector<double>> popularity;
55 std::vector<int> hit_class, miss_class; ///< 0-based destination class, -1 = unset
56
57 // ---- statistics --------------------------------------------------------
58 std::vector<double> hits, misses; ///< per access class
59 std::vector<std::vector<double>> hits_per_list; ///< (access class x list) hits served in that list
60 /**
61 * (item x list) time the item spent in that list, integrated up to `occ_last`: the itemProb and
62 * listCost of `Solver_ssj`, a time average over the same window as the hit counts (the run).
63 */
64 std::vector<std::vector<double>> item_level_time;
65 double occ_last = 0.0;
66
67 /** Integrate the current contents up to `now`; call before every content change and at export. */
68 void accumulate(double now) {
69 const double dt = now - occ_last;
70 if (dt > 0.0)
71 for (std::size_t l = 0; l < lists.size(); ++l)
72 for (std::size_t v : lists[l])
73 if (v < item_level_time.size()) item_level_time[v][l] += dt;
74 occ_last = now;
75 }
76
77 // ---- delayed-hit retrieval system --------------------------------------
78 /**
79 * `Cache.setRetrievalSystem`: a miss FETCHES the item through a sub-network
80 * instead of being served at once, and a second request for an item already
81 * in flight is parked rather than starting a fetch of its own. It is
82 * released when the fetch returns, and counted as a DELAYED HIT: it neither
83 * hit nor missed, and folding it into either loses the whole effect the
84 * model exists to measure.
85 */
86 bool has_retrieval = false;
87 /** (nitems x nclasses) the per-item retrieval class, 0 = none. 1-based. */
88 std::vector<std::vector<std::size_t>> retrieval_class;
89 /** Per class: the item it fetches, or -1 when it is not a retrieval class. */
90 std::vector<int> retrieval_class_to_item;
91 std::vector<char> in_flight; ///< per item, a fetch is outstanding
92 std::vector<double> fetch_start; ///< per item, when that fetch began
93
94 /** A request parked while its item was being fetched. */
95 struct HeldRequest {
96 std::size_t cls = 0; ///< the ACCESS class that read the cache
97 double hold_time = 0.0; ///< when it was parked
98 double t_sys = 0.0; ///< its system arrival time, for response time
99 };
100 std::vector<std::vector<HeldRequest>> held; ///< per item
101
102 std::vector<double> delayed; ///< per access class, delayed-hit count
103 double delayed_wait = 0.0; ///< summed parked time over all releases
104 double total_fetch_time = 0.0; ///< summed fetch duration
105 double completed_fetches = 0.0;
106
107 /** The item `cls` fetches, or -1. */
108 int fetches_item(std::size_t cls) const {
109 return (cls < retrieval_class_to_item.size()) ? retrieval_class_to_item[cls] : -1;
110 }
111 /** The retrieval class of `item` for access class `cls`, or 0. */
112 std::size_t retrieval_of(std::size_t item, std::size_t cls) const {
113 if (item >= retrieval_class.size() || cls >= retrieval_class[item].size()) return 0;
114 return retrieval_class[item][cls];
115 }
116
117 /** The list holding `item`, or -1. */
118 int find(std::size_t item) const {
119 for (std::size_t l = 0; l < lists.size(); ++l)
120 for (std::size_t v : lists[l])
121 if (v == item) return static_cast<int>(l);
122 return -1;
123 }
124
125 double list_cost(std::size_t l) const {
126 if (item_size.empty()) return 0.0;
127 double c = 0.0;
128 for (std::size_t v : lists[l]) c += (v < item_size.size()) ? item_size[v] : 1.0;
129 return c;
130 }
131 double cap_of(std::size_t l) const {
132 return (l < cost_cap.size()) ? cost_cap[l] : -1.0;
133 }
134 bool capped() const { return !item_size.empty() && !cost_cap.empty(); }
135 double size_of(std::size_t item) const {
136 return (item < item_size.size()) ? item_size[item] : 1.0;
137 }
138};
139
140/** True for the policies that refresh recency on a hit. */
145
146/**
147 * Serve a HIT in list `at`.
148 *
149 * A hit PROMOTES the item one list, under EVERY policy, which is the default
150 * access-cost matrix (super-diagonal) the JAR engine and MATLAB afterEventCache
151 * sample from, and the RR(m)/FIFO(m)/LRU(m) chains the NC recursions solve.
152 * Only CLIMB used to promote here, so an RR cache with lists [2, 1] never filled
153 * its second list and reported HitProb 0.361 against the exact 0.539. In the top
154 * list the item stays and the recency policies refresh it. A promotion into a
155 * full list swaps: the item goes to the higher list and the displaced occupant
156 * returns to the lower one, to its head under the recency policies and SFIFO,
157 * and into the slot the item freed under RR and FIFO.
158 */
159inline void cache_hit(CacheState& cs, std::size_t item, std::size_t at, double u_random) {
160 const std::size_t up = (at + 1 < cs.lists.size()) ? at + 1 : at;
161 if (up == at) {
162 if (cache_refreshes(cs.policy)) {
163 cs.lists[at].remove(item);
164 cs.lists[at].push_front(item);
165 }
166 return;
167 }
168 std::list<std::size_t>& lo = cs.lists[at];
169 std::list<std::size_t>& hi = cs.lists[up];
170 if (hi.size() < cs.capacity[up]) {
171 // Warm-up move: the higher list has room, so nothing is demoted.
172 if (cs.capped() && cs.list_cost(up) + cs.size_of(item) > cs.cap_of(up)) return;
173 lo.remove(item);
174 hi.push_front(item);
175 return;
176 }
177 const std::size_t victim =
179 ? *std::next(hi.begin(), static_cast<std::ptrdiff_t>(
180 std::min<std::size_t>(hi.size() - 1,
181 static_cast<std::size_t>(
182 u_random * hi.size()))))
183 : hi.back();
184 if (cs.capped()) {
185 const double hi_after = cs.list_cost(up) - cs.size_of(victim) + cs.size_of(item);
186 const double lo_after = cs.list_cost(at) - cs.size_of(item) + cs.size_of(victim);
187 // SERVED, NOT REFUSED: a swap that breaches either cap leaves the cache
188 // untouched and the request goes through anyway.
189 if (hi_after > cs.cap_of(up) || (cs.cap_of(at) >= 0.0 && lo_after > cs.cap_of(at))) return;
190 }
191 auto slot = std::find(lo.begin(), lo.end(), item);
192 slot = lo.erase(slot); // the position the item frees in the lower list
193 hi.remove(victim);
194 hi.push_front(item);
196 lo.push_front(victim);
197 else
198 lo.insert(slot, victim);
199}
200
201/** Serve a MISS: insert into the entry list, evicting if it is full. */
202inline void cache_miss(CacheState& cs, std::size_t item, double u_admit, double u_random) {
203 if (cs.lists.empty()) return;
204 const std::size_t target = 0; // the entry list
205 std::list<std::size_t>& list = cs.lists[target];
206 const std::size_t cap = cs.capacity[target];
207 // q-LRU admits only a fraction of misses, which is the whole policy.
208 if (cs.policy == lang::ReplacementStrategy::QLRU && u_admit > cs.qlru) return;
209
210 if (cs.policy == lang::ReplacementStrategy::RR && list.size() >= cap && !list.empty()) {
211 const std::size_t pos =
212 std::min<std::size_t>(list.size() - 1, static_cast<std::size_t>(u_random * list.size()));
213 auto it = std::next(list.begin(), static_cast<std::ptrdiff_t>(pos));
214 if (cs.capped() &&
215 cs.list_cost(target) - cs.size_of(*it) + cs.size_of(item) > cs.cap_of(target))
216 return;
217 *it = item;
218 return;
219 }
220 if (cs.capped()) {
221 double resulting = cs.list_cost(target) + cs.size_of(item);
222 if (list.size() + 1 > cap && !list.empty()) resulting -= cs.size_of(list.back());
223 if (resulting > cs.cap_of(target)) return;
224 }
225 list.push_front(item);
226 if (list.size() > cap) list.pop_back();
227}
228
229} // namespace engine
230} // namespace ldes
231} // namespace line
232
233#endif // LINE_SOLVERS_LDES_LDES_CACHE_H
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
Definition lang_types.h:385
@ HLRU
h-LRU / LRU(m): h lists, promote i -> i+1 on a hit
Definition lang_types.h:390
@ QLRU
q-LRU: LRU with probabilistic admission on a miss
Definition lang_types.h:392
@ LRU
least recently used
Definition lang_types.h:389
void cache_hit(CacheState &cs, std::size_t item, std::size_t at, double u_random)
Serve a HIT in list at.
Definition ldes_cache.h:159
void cache_miss(CacheState &cs, std::size_t item, double u_admit, double u_random)
Serve a MISS: insert into the entry list, evicting if it is full.
Definition ldes_cache.h:202
bool cache_refreshes(lang::ReplacementStrategy p)
True for the policies that refresh recency on a hit.
Definition ldes_cache.h:141
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A request parked while its item was being fetched.
Definition ldes_cache.h:95
double t_sys
its system arrival time, for response time
Definition ldes_cache.h:98
std::size_t cls
the ACCESS class that read the cache
Definition ldes_cache.h:96
Live state of one Cache node.
Definition ldes_cache.h:44
std::vector< double > misses
per access class
Definition ldes_cache.h:58
double cap_of(std::size_t l) const
Definition ldes_cache.h:131
int find(std::size_t item) const
The list holding item, or -1.
Definition ldes_cache.h:118
void accumulate(double now)
Integrate the current contents up to now; call before every content change and at export.
Definition ldes_cache.h:68
std::vector< int > hit_class
Definition ldes_cache.h:55
std::vector< double > item_size
empty = unconstrained
Definition ldes_cache.h:51
bool has_retrieval
Cache.setRetrievalSystem: a miss FETCHES the item through a sub-network instead of being served at on...
Definition ldes_cache.h:86
double delayed_wait
summed parked time over all releases
Definition ldes_cache.h:103
double size_of(std::size_t item) const
Definition ldes_cache.h:135
double qlru
q-LRU admission probability on a miss
Definition ldes_cache.h:48
std::vector< double > cost_cap
empty = unconstrained
Definition ldes_cache.h:52
double list_cost(std::size_t l) const
Definition ldes_cache.h:125
std::vector< std::list< std::size_t > > lists
per list, most recent at the front
Definition ldes_cache.h:50
std::vector< double > delayed
per access class, delayed-hit count
Definition ldes_cache.h:102
std::vector< std::vector< double > > hits_per_list
(access class x list) hits served in that list
Definition ldes_cache.h:59
int fetches_item(std::size_t cls) const
The item cls fetches, or -1.
Definition ldes_cache.h:108
std::vector< int > miss_class
0-based destination class, -1 = unset
Definition ldes_cache.h:55
std::size_t retrieval_of(std::size_t item, std::size_t cls) const
The retrieval class of item for access class cls, or 0.
Definition ldes_cache.h:112
lang::ReplacementStrategy policy
Definition ldes_cache.h:47
std::vector< double > fetch_start
per item, when that fetch began
Definition ldes_cache.h:92
std::vector< std::vector< double > > popularity
Per access class: the item popularity, as a cumulative law.
Definition ldes_cache.h:54
std::vector< std::size_t > capacity
per list
Definition ldes_cache.h:49
std::vector< int > retrieval_class_to_item
Per class: the item it fetches, or -1 when it is not a retrieval class.
Definition ldes_cache.h:90
double total_fetch_time
summed fetch duration
Definition ldes_cache.h:104
std::vector< std::vector< double > > item_level_time
(item x list) time the item spent in that list, integrated up to occ_last: the itemProb and listCost ...
Definition ldes_cache.h:64
std::vector< double > hits
Definition ldes_cache.h:58
std::vector< char > in_flight
per item, a fetch is outstanding
Definition ldes_cache.h:91
std::vector< std::vector< std::size_t > > retrieval_class
(nitems x nclasses) the per-item retrieval class, 0 = none.
Definition ldes_cache.h:88
std::vector< std::vector< HeldRequest > > held
per item
Definition ldes_cache.h:100