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
60 // ---- delayed-hit retrieval system --------------------------------------
61 /**
62 * `Cache.setRetrievalSystem`: a miss FETCHES the item through a sub-network
63 * instead of being served at once, and a second request for an item already
64 * in flight is parked rather than starting a fetch of its own. It is
65 * released when the fetch returns, and counted as a DELAYED HIT: it neither
66 * hit nor missed, and folding it into either loses the whole effect the
67 * model exists to measure.
68 */
69 bool has_retrieval = false;
70 /** (nitems x nclasses) the per-item retrieval class, 0 = none. 1-based. */
71 std::vector<std::vector<std::size_t>> retrieval_class;
72 /** Per class: the item it fetches, or -1 when it is not a retrieval class. */
73 std::vector<int> retrieval_class_to_item;
74 std::vector<char> in_flight; ///< per item, a fetch is outstanding
75 std::vector<double> fetch_start; ///< per item, when that fetch began
76
77 /** A request parked while its item was being fetched. */
78 struct HeldRequest {
79 std::size_t cls = 0; ///< the ACCESS class that read the cache
80 double hold_time = 0.0; ///< when it was parked
81 double t_sys = 0.0; ///< its system arrival time, for response time
82 };
83 std::vector<std::vector<HeldRequest>> held; ///< per item
84
85 std::vector<double> delayed; ///< per access class, delayed-hit count
86 double delayed_wait = 0.0; ///< summed parked time over all releases
87 double total_fetch_time = 0.0; ///< summed fetch duration
88 double completed_fetches = 0.0;
89
90 /** The item `cls` fetches, or -1. */
91 int fetches_item(std::size_t cls) const {
92 return (cls < retrieval_class_to_item.size()) ? retrieval_class_to_item[cls] : -1;
93 }
94 /** The retrieval class of `item` for access class `cls`, or 0. */
95 std::size_t retrieval_of(std::size_t item, std::size_t cls) const {
96 if (item >= retrieval_class.size() || cls >= retrieval_class[item].size()) return 0;
97 return retrieval_class[item][cls];
98 }
99
100 /** The list holding `item`, or -1. */
101 int find(std::size_t item) const {
102 for (std::size_t l = 0; l < lists.size(); ++l)
103 for (std::size_t v : lists[l])
104 if (v == item) return static_cast<int>(l);
105 return -1;
106 }
107
108 double list_cost(std::size_t l) const {
109 if (item_size.empty()) return 0.0;
110 double c = 0.0;
111 for (std::size_t v : lists[l]) c += (v < item_size.size()) ? item_size[v] : 1.0;
112 return c;
113 }
114 double cap_of(std::size_t l) const {
115 return (l < cost_cap.size()) ? cost_cap[l] : -1.0;
116 }
117 bool capped() const { return !item_size.empty() && !cost_cap.empty(); }
118 double size_of(std::size_t item) const {
119 return (item < item_size.size()) ? item_size[item] : 1.0;
120 }
121};
122
123/** True for the policies that refresh recency on a hit. */
128
129/**
130 * Serve a HIT in list `at`.
131 *
132 * CLIMB promotes one list per hit; the flat policies keep the item where it is
133 * and only refresh its recency. A promotion into a full list swaps: the item
134 * goes to the head of the higher list and the displaced occupant returns to the
135 * lower one.
136 */
137inline void cache_hit(CacheState& cs, std::size_t item, std::size_t at, double u_random) {
138 const bool climb = (cs.policy == lang::ReplacementStrategy::CLIMB);
139 const std::size_t up = (climb && at + 1 < cs.lists.size()) ? at + 1 : at;
140 if (up == at) {
141 if (cache_refreshes(cs.policy)) {
142 cs.lists[at].remove(item);
143 cs.lists[at].push_front(item);
144 }
145 return;
146 }
147 std::list<std::size_t>& lo = cs.lists[at];
148 std::list<std::size_t>& hi = cs.lists[up];
149 if (hi.size() < cs.capacity[up]) {
150 // Warm-up move: the higher list has room, so nothing is demoted.
151 if (cs.capped() && cs.list_cost(up) + cs.size_of(item) > cs.cap_of(up)) return;
152 lo.remove(item);
153 hi.push_front(item);
154 return;
155 }
156 const std::size_t victim =
158 ? *std::next(hi.begin(), static_cast<std::ptrdiff_t>(
159 std::min<std::size_t>(hi.size() - 1,
160 static_cast<std::size_t>(
161 u_random * hi.size()))))
162 : hi.back();
163 if (cs.capped()) {
164 const double hi_after = cs.list_cost(up) - cs.size_of(victim) + cs.size_of(item);
165 const double lo_after = cs.list_cost(at) - cs.size_of(item) + cs.size_of(victim);
166 // SERVED, NOT REFUSED: a swap that breaches either cap leaves the cache
167 // untouched and the request goes through anyway.
168 if (hi_after > cs.cap_of(up) || (cs.cap_of(at) >= 0.0 && lo_after > cs.cap_of(at))) return;
169 }
170 lo.remove(item);
171 hi.remove(victim);
172 hi.push_front(item);
174 lo.push_front(victim);
175 else
176 lo.push_back(victim);
177}
178
179/** Serve a MISS: insert into the entry list, evicting if it is full. */
180inline void cache_miss(CacheState& cs, std::size_t item, double u_admit, double u_random) {
181 if (cs.lists.empty()) return;
182 const std::size_t target = 0; // the entry list
183 std::list<std::size_t>& list = cs.lists[target];
184 const std::size_t cap = cs.capacity[target];
185 // q-LRU admits only a fraction of misses, which is the whole policy.
186 if (cs.policy == lang::ReplacementStrategy::QLRU && u_admit > cs.qlru) return;
187
188 if (cs.policy == lang::ReplacementStrategy::RR && list.size() >= cap && !list.empty()) {
189 const std::size_t pos =
190 std::min<std::size_t>(list.size() - 1, static_cast<std::size_t>(u_random * list.size()));
191 auto it = std::next(list.begin(), static_cast<std::ptrdiff_t>(pos));
192 if (cs.capped() &&
193 cs.list_cost(target) - cs.size_of(*it) + cs.size_of(item) > cs.cap_of(target))
194 return;
195 *it = item;
196 return;
197 }
198 if (cs.capped()) {
199 double resulting = cs.list_cost(target) + cs.size_of(item);
200 if (list.size() + 1 > cap && !list.empty()) resulting -= cs.size_of(list.back());
201 if (resulting > cs.cap_of(target)) return;
202 }
203 list.push_front(item);
204 if (list.size() > cap) list.pop_back();
205}
206
207} // namespace engine
208} // namespace ldes
209} // namespace line
210
211#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:378
@ HLRU
h-LRU / LRU(m): h lists, promote i -> i+1 on a hit
Definition lang_types.h:383
@ CLIMB
move up one position on a hit (transposition rule)
Definition lang_types.h:384
@ QLRU
q-LRU: LRU with probabilistic admission on a miss
Definition lang_types.h:385
@ LRU
least recently used
Definition lang_types.h:382
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:137
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:180
bool cache_refreshes(lang::ReplacementStrategy p)
True for the policies that refresh recency on a hit.
Definition ldes_cache.h:124
A request parked while its item was being fetched.
Definition ldes_cache.h:78
double t_sys
its system arrival time, for response time
Definition ldes_cache.h:81
std::size_t cls
the ACCESS class that read the cache
Definition ldes_cache.h:79
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:114
int find(std::size_t item) const
The list holding item, or -1.
Definition ldes_cache.h:101
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:69
double delayed_wait
summed parked time over all releases
Definition ldes_cache.h:86
double size_of(std::size_t item) const
Definition ldes_cache.h:118
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:108
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:85
int fetches_item(std::size_t cls) const
The item cls fetches, or -1.
Definition ldes_cache.h:91
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:95
lang::ReplacementStrategy policy
Definition ldes_cache.h:47
std::vector< double > fetch_start
per item, when that fetch began
Definition ldes_cache.h:75
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:73
double total_fetch_time
summed fetch duration
Definition ldes_cache.h:87
std::vector< double > hits
Definition ldes_cache.h:58
std::vector< char > in_flight
per item, a fetch is outstanding
Definition ldes_cache.h:74
std::vector< std::vector< std::size_t > > retrieval_class
(nitems x nclasses) the per-item retrieval class, 0 = none.
Definition ldes_cache.h:71
std::vector< std::vector< HeldRequest > > held
per item
Definition ldes_cache.h:83