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
46#include "line/util/error.h"
47
48namespace line {
49namespace nc {
50
51/** What the integrated analyzer returns: the metrics plus the converged split. */
52template <class T>
55 Matrix<T> hitprob; ///< (ncaches x nclasses) converged hit probability
56 Matrix<T> missprob; ///< (ncaches x nclasses)
57 std::vector<Matrix<T> > itemprob; ///< per cache, (n x h+1); EMPTY = not computed
58 /**
59 * The struct whose cache self-switch carries the CONVERGED split rather
60 * than the offered one, from which the runner derives ArvR and ResidT.
61 */
63};
64
65/**
66 * Port of `solver_nc_cacheqn_analyzer.m`.
67 *
68 * @param sn the refreshed struct, carrying at least one Cache node
69 * @param opt solver controls; `method == "exact"` selects the exact recursion
70 */
71template <class T>
73 const NcSolverOptions& opt) {
75 if constexpr (!num_traits<T>::has_transcendental) {
76 (void)sn;
77 (void)opt;
78 throw UnsupportedError(
79 "solver_nc_cacheqn_analyzer: the integrated caching-queueing decomposition alternates "
80 "two tolerance-stopped solves and needs transcendental arithmetic");
81 } else {
82 const T zero = num_traits<T>::from_int(0);
83 const T one = num_traits<T>::from_int(1);
84
85 // The reference's admissibility gate, per cache. Note it compares
86 // against the SUM of the list capacities here, where the non-reentrant
87 // analyzer compares against the capacity vector entrywise.
88 for (const auto& kv : sn.nodeparam) {
89 const qn::CacheParam<T>& ch = kv.second;
90 long capsum = 0;
91 for (int c : ch.itemcap) capsum += c;
92 if (static_cast<long>(ch.nitems) < capsum + 2)
93 throw UnsupportedError(
94 "solver_nc_cacheqn_analyzer: NC requires the number of items to exceed the "
95 "cache capacity at least by 2; this cache holds " +
96 std::to_string(ch.nitems) + " items with total capacity " +
97 std::to_string(capsum));
98 }
99
100 const bool exact = (opt.method == "exact");
101 // EXACT IS EXACT ONLY FOR THE EXCHANGEABLE FAMILY, and this analyzer used
102 // to take `--method exact` on any policy: `cache_prob_erec` would then
103 // return the RR/FIFO answer for an LRU cache SILENTLY, which is the one
104 // outcome `solver_nc_cache_analyzer` refuses by name for the isolated
105 // model (see its guard, same argument and near-identical wording). The
106 // two analyzers differ in whether the cache is embedded in a queueing
107 // network, not in which replacement policies the recursion is valid for.
108 if (exact) {
109 for (const auto& kv : sn.nodeparam) {
110 const qn::CacheParam<T>& ch = kv.second;
113 throw UnsupportedError(
114 "solver_nc_cacheqn_analyzer: NC does not support the exact solution of "
115 "this cache replacement policy -- only RR and FIFO are exchangeable, and "
116 "a recency-based policy (LRU, h-LRU, q-LRU, CLIMB) would silently receive "
117 "the exchangeable answer. Use the default (approximate) method or "
118 "SolverCTMC");
119 }
120 }
121 const NcSolverOptions netopt = opt;
122
123 // The network solver: the ordinary NC analyzer, or the load-dependent
124 // one when the model carries scaling. Matches the reference's netsolve.
125 std::function<mva::MvaSolution<T>(const qn::NetworkStruct<T>&)> netfun =
126 [netopt](const qn::NetworkStruct<T>& snit) -> mva::MvaSolution<T> {
127 return nc_dispatch(snit, netopt).sol;
128 };
129
130 // The isolated-cache miss algorithm, which is what distinguishes this
131 // analyzer from SolverMVA's.
132 std::function<std::vector<T>(const Matrix<T>&, const std::vector<int>&,
133 const std::vector<Matrix<T> >&, const qn::CacheParam<T>&)>
134 missfun = [exact](const Matrix<T>& gamma, const std::vector<int>& m,
135 const std::vector<Matrix<T> >& lambda_cache,
136 const qn::CacheParam<T>& ch) -> std::vector<T> {
137 (void)ch; // the erec/spm pair reads the cache through `gamma` alone
138 const std::size_t u = lambda_cache.size();
139 std::vector<T> missrate(u, num_traits<T>::from_int(0));
140 if (u == 0) return missrate;
141 const std::size_t n = lambda_cache[0].rows();
142 if (exact) {
143 const Matrix<T> pij = cache::cache_prob_erec(gamma, m);
144 for (std::size_t v = 0; v < u; ++v) {
145 T acc = num_traits<T>::from_int(0);
146 for (std::size_t k = 0; k < n && k < pij.rows(); ++k)
147 acc = T(acc + lambda_cache[v](k, 0) * pij(k, 0));
148 missrate[v] = acc;
149 }
150 } else {
151 Matrix<T> lam_un(u, n, num_traits<T>::from_int(0));
152 for (std::size_t v = 0; v < u; ++v)
153 for (std::size_t k = 0; k < n; ++k) lam_un(v, k) = lambda_cache[v](k, 0);
154 const cache::CacheMissSpmResult<T> mr = cache::cache_miss_spm(gamma, m, lam_un);
155 for (std::size_t v = 0; v < mr.MU.size() && v < u; ++v) missrate[v] = mr.MU[v];
156 }
157 return missrate;
158 };
159
160 mva::MvaOptions mopt;
161 mopt.method = opt.method;
162 mopt.tol = opt.tol;
163 mopt.iter_tol = opt.iter_tol;
164 mopt.iter_max = opt.iter_max;
165
166 const da::CacheqnResult<T> r = da::da_cacheqn<T>(sn, exact, mopt, netfun, missfun);
167 out.sol.sol = r.res;
168 out.sol.sol.iter = r.iter;
169 out.sol.actualmethod = exact ? "exact" : "spm";
170 out.sol.sol.method = out.sol.actualmethod;
171 out.hitprob = r.hitprob;
172 out.missprob = r.missprob;
173 // Per-item occupancy from the CONVERGED access factors, as SolverMVA reports it.
175 // The runner reads ArvR and ResidT off a struct whose cache self-switch
176 // carries the CONVERGED split, not the offered one.
177 out.refreshed = sn;
178 out.refreshed.refresh_cacheqn_actual_visits(r.hitprob, r.missprob);
179 (void)zero;
180 (void)one;
181 return out;
182 }
183}
184
185/** True when the model has a Cache node and is not the Source-Cache-Sink shape. */
186template <class T>
188 bool hasCache = false;
189 for (const qn::NodeDef& nd : sn.nodes)
190 if (nd.nodetype == qn::NodeType::Cache) hasCache = true;
191 return hasCache;
192}
193
194} // namespace nc
195} // namespace line
196
197#endif // LINE_SOLVERS_NC_SOLVER_NC_CACHEQN_H
Saddle-point approximation of the cache miss rates.
Exact per-item hit and miss probabilities of a multi-list cache.
std::size_t size() const
Definition matrix.h:91
std::size_t rows() const
Definition matrix.h:89
UnsupportedError(const std::string &what)
Definition error.h:51
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.
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.
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 > &)> &missfun=nullptr)
Decomposition-aggregation driver for integrated cache-queueing models, a port of matlab/src/api/da/da...
Definition da_cacheqn.h:133
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:82
@ FIFO
first in, first out
Definition lang_types.h:380
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.
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...
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.
Return value of cache_miss_spm, mirroring [M,MU,MI,pi0,lE].
Matrix< T > missprob
(ncaches x nclasses)
Definition da_cacheqn.h:61
Matrix< T > hitprob
(ncaches x nclasses)
Definition da_cacheqn.h:60
CacheqnInfo< T > info
Definition da_cacheqn.h:63
mva::MvaSolution< T > res
Definition da_cacheqn.h:59
The options SolverMVA reads.
Definition mva_types.h:31
std::string method
Definition mva_types.h:32
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
Definition mva_types.h:96
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< 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
std::vector< int > itemcap
lang::ReplacementStrategy replacestrat
A node of the network.