LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_nc_cacheqn.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_NC_SOLVER_NC_CACHEQN_H
6#define LINE_SOLVERS_NC_SOLVER_NC_CACHEQN_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Port of `solver_nc_cacheqn_analyzer.m`: the INTEGRATED caching-queueing
12 * network, where a Cache sits inside a queueing network rather than between a
13 * Source and a Sink.
14 *
15 * WHY THIS NEEDS A FIXED POINT AND THE NON-REENTRANT CACHE DOES NOT. There, the
16 * read rate is the Source's and is known up front. Here the flow reaching the
17 * cache depends on the queueing network's throughputs, which depend in turn on
18 * how the cache splits that flow between its hit and miss classes. So the two
19 * are alternated: solve the caches in ISOLATION at the current arrival rates,
20 * relabel the routing with the resulting hit/miss split, solve the queueing
21 * network, and repeat. That alternation is `da_cacheqn`, shared with SolverMVA.
22 *
23 * WHAT THIS FILE SUPPLIES, and it is only two things: the isolated-cache MISS
24 * ALGORITHM and the NETWORK SOLVER. Both differ from SolverMVA's:
25 *
26 * miss exact -> `cache_prob_erec`, otherwise `cache_miss_spm`
27 * (SolverMVA uses `cache_mva` / `cache_miss_fpi`)
28 * network `solver_nc_analyzer`, or `solver_ncld_analyzer` under scaling
29 * (SolverMVA uses its own analyzers)
30 *
31 * THE DRIVER TAKES A HANDLE AGAIN. `da_cacheqn.m` has always taken `missfun` as
32 * an argument; the C++ driver had collapsed it to a boolean, which hardcoded
33 * SolverMVA's pair into a function both solvers share. The handle is restored,
34 * defaulting to the MVA pair so that solver's call site is unchanged.
35 */
36
37#include <functional>
38#include <vector>
39
48#include "line/util/error.h"
49
50namespace line {
51namespace nc {
52
53/** What the integrated analyzer returns: the metrics plus the converged split. */
54template <class T>
57 Matrix<T> hitprob; ///< (ncaches x nclasses) converged hit probability
58 Matrix<T> missprob; ///< (ncaches x nclasses)
59 std::vector<Matrix<T> > itemprob; ///< per cache, (n x h+1); EMPTY = not computed
60 std::vector<std::vector<T> > listcost; ///< per cache, (h) mean storage cost per list; EMPTY without item sizes
61 /**
62 * The struct whose cache self-switch carries the CONVERGED split rather
63 * than the offered one, from which the runner derives ArvR and ResidT.
64 */
66};
67
68/**
69 * Port of `solver_nc_cacheqn_analyzer.m`.
70 *
71 * @param sn the refreshed struct, carrying at least one Cache node
72 * @param opt solver controls; `method == "exact"` selects the exact recursion
73 */
74template <class T>
76 const NcSolverOptions& opt) {
78 if constexpr (!num_traits<T>::has_transcendental) {
79 (void)sn;
80 (void)opt;
81 throw UnsupportedError(ErrorKind{"Arithmetic"},
82 "solver_nc_cacheqn_analyzer: the integrated caching-queueing decomposition alternates "
83 "two tolerance-stopped solves and needs transcendental arithmetic");
84 } else {
85 const T zero = num_traits<T>::from_int(0);
86 const T one = num_traits<T>::from_int(1);
87
88 // The reference's admissibility gate, per cache. Note it compares
89 // against the SUM of the list capacities here, where the non-reentrant
90 // analyzer compares against the capacity vector entrywise.
91 for (const auto& kv : sn.nodeparam) {
92 const qn::CacheParam<T>& ch = kv.second;
93 long capsum = 0;
94 for (int c : ch.itemcap) capsum += c;
95 if (static_cast<long>(ch.nitems) < capsum + 2)
96 throw UnsupportedError(ErrorKind{"Cache"},
97 "solver_nc_cacheqn_analyzer: NC requires the number of items to exceed the "
98 "cache capacity at least by 2; this cache holds " +
99 std::to_string(ch.nitems) + " items with total capacity " +
100 std::to_string(capsum));
101 }
102
103 const bool exact = (opt.method == "exact");
104 // EXACT IS EXACT ONLY FOR THE EXCHANGEABLE FAMILY, and this analyzer used
105 // to take `--method exact` on any policy: `cache_prob_erec` would then
106 // return the RR/FIFO answer for an LRU cache SILENTLY, which is the one
107 // outcome `solver_nc_cache_analyzer` refuses by name for the isolated
108 // model (see its guard, same argument and near-identical wording). The
109 // two analyzers differ in whether the cache is embedded in a queueing
110 // network, not in which replacement policies the recursion is valid for.
111 if (exact) {
112 for (const auto& kv : sn.nodeparam) {
113 const qn::CacheParam<T>& ch = kv.second;
116 throw UnsupportedError(ErrorKind{"Cache"},
117 "solver_nc_cacheqn_analyzer: NC does not support the exact solution of "
118 "this cache replacement policy -- only RR and FIFO are exchangeable, and "
119 "a recency-based policy (LRU, h-LRU, q-LRU, CLIMB) would silently receive "
120 "the exchangeable answer. Use the default (approximate) method or "
121 "SolverCTMC");
122 }
123 }
124 const NcSolverOptions netopt = opt;
125 std::string sizedMethod; // set by missfun when a cache carries item sizes
126
127 // The network solver: the ordinary NC analyzer, or the load-dependent
128 // one when the model carries scaling. Matches the reference's netsolve.
129 std::function<mva::MvaSolution<T>(const qn::NetworkStruct<T>&)> netfun =
130 [netopt](const qn::NetworkStruct<T>& snit) -> mva::MvaSolution<T> {
131 return nc_dispatch(snit, netopt).sol;
132 };
133
134 // The isolated-cache miss algorithm, which is what distinguishes this
135 // analyzer from SolverMVA's.
136 std::function<std::vector<T>(const Matrix<T>&, const std::vector<int>&,
137 const std::vector<Matrix<T> >&, const qn::CacheParam<T>&,
138 const std::vector<int>&)>
139 missfun = [exact, netopt, &sizedMethod](const Matrix<T>& gamma, const std::vector<int>& m,
140 const std::vector<Matrix<T> >& lambda_cache,
141 const qn::CacheParam<T>& ch,
142 const std::vector<int>& parent) -> std::vector<T> {
143 const std::size_t u = lambda_cache.size();
144 std::vector<T> missrate(u, num_traits<T>::from_int(0));
145 if (u == 0) return missrate;
146 const std::size_t n = lambda_cache[0].rows();
147 if (!ch.itemsize.empty()) {
148 // item sizes and cost caps: the same method selection as the isolated
149 // cache, which the size-free erec/spm pair below would silently ignore
150 const NcCacheMiss<T> cm = nc_cache_missrate(gamma, parent, m, n, lambda_cache, ch, netopt);
151 sizedMethod = cm.method;
152 return cm.missRate;
153 }
154 if (exact) {
155 const Matrix<T> pij = cache::cache_prob_erec(gamma, m);
156 for (std::size_t v = 0; v < u; ++v) {
157 T acc = num_traits<T>::from_int(0);
158 for (std::size_t k = 0; k < n && k < pij.rows(); ++k)
159 acc = T(acc + lambda_cache[v](k, 0) * pij(k, 0));
160 missrate[v] = acc;
161 }
162 } else {
163 Matrix<T> lam_un(u, n, num_traits<T>::from_int(0));
164 for (std::size_t v = 0; v < u; ++v)
165 for (std::size_t k = 0; k < n; ++k) lam_un(v, k) = lambda_cache[v](k, 0);
166 const cache::CacheMissSpmResult<T> mr = cache::cache_miss_spm(gamma, m, lam_un);
167 for (std::size_t v = 0; v < mr.MU.size() && v < u; ++v) missrate[v] = mr.MU[v];
168 }
169 return missrate;
170 };
171
172 mva::MvaOptions mopt;
173 mopt.method = opt.method;
174 mopt.tol = opt.tol;
175 mopt.iter_tol = opt.iter_tol;
176 mopt.iter_max = opt.iter_max;
177
178 const da::CacheqnResult<T> r = da::da_cacheqn<T>(sn, exact, mopt, netfun, missfun);
179 out.sol.sol = r.res;
180 out.sol.sol.iter = r.iter;
181 out.sol.actualmethod = !sizedMethod.empty() ? sizedMethod : (exact ? "exact" : "spm");
182 out.sol.sol.method = out.sol.actualmethod;
183 out.hitprob = r.hitprob;
184 out.missprob = r.missprob;
185 // Per-item occupancy from the CONVERGED access factors, as SolverMVA reports it.
187 // Mean storage cost held by each list, K_j = sum_i sigma_i pi_ij, per sized cache, as the isolated cache reports it.
188 out.listcost.assign(r.info.gamma.size(), std::vector<T>());
189 for (std::size_t ci = 0; ci < r.info.gamma.size() && ci < out.itemprob.size(); ++ci) {
190 const std::vector<int>& sigma = r.info.itemsize[ci];
191 const Matrix<T>& ip = out.itemprob[ci];
192 if (!sigma.empty() && ip.rows() == sigma.size() && ip.cols() == r.info.itemcap[ci].size() + 1)
193 out.listcost[ci] = cache::cache_cost(r.info.gamma[ci], r.info.itemcap[ci], sigma,
194 r.info.costcap[ci], ip);
195 }
196 // The runner reads ArvR and ResidT off a struct whose cache self-switch
197 // carries the CONVERGED split, not the offered one.
198 out.refreshed = sn;
199 out.refreshed.refresh_cacheqn_actual_visits(r.hitprob, r.missprob);
200 (void)zero;
201 (void)one;
202 return out;
203 }
204}
205
206/** True when the model has a Cache node and is not the Source-Cache-Sink shape. */
207template <class T>
209 bool hasCache = false;
210 for (const qn::NodeDef& nd : sn.nodes)
211 if (nd.nodetype == qn::NodeType::Cache) hasCache = true;
212 return hasCache;
213}
214
215} // namespace nc
216} // namespace line
217
218#endif // LINE_SOLVERS_NC_SOLVER_NC_CACHEQN_H
Mean per-list storage cost of a cache with item sizes, and the screen for promotion paths that storag...
Saddle-point approximation of the cache miss rates.
Exact per-item hit and miss probabilities of a multi-list cache.
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
UnsupportedError(const std::string &what)
Definition error.h:119
A network plus its refreshed NetworkStruct.
Decomposition-aggregation driver for integrated cache-queueing models, a port of matlab/src/api/da/da...
The exception types the port throws.
std::vector< T > cache_cost(const Matrix< T > &gamma, const std::vector< int > &m, const std::vector< int > &sigma, const std::vector< int > &k, const Matrix< T > &pij)
Mean per-list storage cost of a cache with item sizes, and the screen for promotion paths that storag...
Definition cache_cost.h:63
Matrix< T > cache_prob_erec(const Matrix< T > &gamma, const std::vector< int > &m, const std::vector< int > &sigma, const std::vector< int > &k)
Per-item hit and miss probabilities under per-list storage cost caps, pi_ij = m_j gamma(i,...
CacheMissSpmResult< T > cache_miss_spm(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda)
Saddle-point approximation of the cache miss rates.
std::vector< Matrix< T > > da_cacheqn_itemprob(const CacheqnInfo< T > &info)
Per-item occupancy of every cache from the CONVERGED access factors.
Definition da_cacheqn.h:86
CacheqnResult< T > da_cacheqn(qn::NetworkStruct< T > sn, bool exact, const mva::MvaOptions &opt, const std::function< mva::MvaSolution< T >(const qn::NetworkStruct< T > &)> &netfun, const std::function< std::vector< T >(const Matrix< T > &, const std::vector< int > &, const std::vector< Matrix< T > > &, const qn::CacheParam< T > &, const std::vector< int > &)> &missfun=nullptr)
Decomposition-aggregation driver for integrated cache-queueing models, a port of matlab/src/api/da/da...
Definition da_cacheqn.h:138
@ FIFO
first in, first out
Definition lang_types.h:387
bool nc_is_cacheqn(const qn::NetworkStruct< T > &sn)
True when the model has a Cache node and is not the Source-Cache-Sink shape.
NcCacheMiss< T > nc_cache_missrate(const Matrix< T > &gamma, const std::vector< int > &parent, const std::vector< int > &m, std::size_t n, const std::vector< Matrix< T > > &lambda, const qn::CacheParam< T > &ch, const NcSolverOptions &opt)
Miss rates of an isolated cache under opt.method, honouring the per-item storage costs and per-list c...
NcCacheqnSolution< T > solver_nc_cacheqn_analyzer(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of solver_nc_cacheqn_analyzer.m.
NcSolution< T > nc_dispatch(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of @@SolverNC/ncDispatch.m: the inner solve of the fork-join fixed point, which is the load-depe...
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Port of solver_nc_analyzer.m, solver_ncld_analyzer.m and @@SolverNC/ncDispatch.m: one inner solve,...
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Port of solver_nc_cache_analyzer.m: the NON-REENTRANT cache, a model that is exactly a Source,...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
Return value of cache_miss_spm, mirroring [M,MU,MI,pi0,lE].
Matrix< T > missprob
(ncaches x nclasses)
Definition da_cacheqn.h:65
Matrix< T > hitprob
(ncaches x nclasses)
Definition da_cacheqn.h:64
CacheqnInfo< T > info
Definition da_cacheqn.h:67
mva::MvaSolution< T > res
Definition da_cacheqn.h:63
The options SolverMVA reads.
Definition mva_types.h:32
std::string method
Definition mva_types.h:33
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
Definition mva_types.h:97
Miss rates of one isolated cache and what produced them; see nc_cache_missrate.
std::vector< T > missRate
(u) miss rate of each user class
What the integrated analyzer returns: the metrics plus the converged split.
Matrix< T > hitprob
(ncaches x nclasses) converged hit probability
Matrix< T > missprob
(ncaches x nclasses)
qn::NetworkStruct< T > refreshed
The struct whose cache self-switch carries the CONVERGED split rather than the offered one,...
std::vector< std::vector< T > > listcost
per cache, (h) mean storage cost per list; EMPTY without item sizes
std::vector< Matrix< T > > itemprob
per cache, (n x h+1); EMPTY = not computed
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.
Definition nc_types.h:113
Controls, defaulting to SolverOptions('NC') in the reference.
Definition nc_types.h:33
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< int > itemcap
lang::ReplacementStrategy replacestrat
A node of the network.