LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
cache_mva.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_API_CACHE_MVA_H
6#define LINE_API_CACHE_MVA_H
7
8/**
9 * @file
10 * @ingroup api_cache
11 * Exact mean value analysis of a multi-list cache.
12 *
13 * Templated port of matlab/src/api/cache/cache_mva.m, cross-checked against
14 * jar/src/main/java/jline/api/cache/Cache_mva.java.
15 *
16 * Sweeps the box of cache occupancies {0..m(1)} x ... x {0..m(h)} in the order
17 * MATLAB's State.cartesian produces (first list varying fastest, so the
18 * predecessor state m - e_l always precedes m). At each state the MVA
19 * arrival-theorem step is
20 *
21 * x(l) = m(l) / sum_k gamma(k,l) (1 - pi(k; m - e_l))
22 * pij(k,l) = gamma(k,l) (1 - pi(k; m - e_l)) x(l)
23 * pi(k) = sum_l pij(k,l),
24 *
25 * i.e. the list-l throughput normalizes the "item k is not already cached"
26 * probabilities seen at the previous population. Only divisions and products,
27 * so the exact instantiation carries the whole sweep in rationals; the
28 * conservation law sum_l pij(k,l) + pi0(k) = 1 then holds identically.
29 *
30 * REFERENCE DEFECT (both codebases): the normalizing constant E of the return
31 * list is initialized to 1 and never updated -- MATLAB sets `E=1;` before the
32 * sweep and returns it, and the JAR mirrors that with `int E = 1;`. The value
33 * is therefore not the cache normalizing constant (use cache_erec for that).
34 * It is reproduced here for interface compatibility and flagged in the result
35 * struct, not silently recomputed, since callers may rely on the constant.
36 */
37
38#include <cstddef>
39#include <vector>
40
41#include "line/num/number.h"
42#include "line/util/error.h"
43#include "line/util/matrix.h"
44
45namespace line {
46namespace cache {
47
48/** Return value of cache_mva, mirroring [pi,pi0,pij,x,u,E]. */
49template <class T>
51 std::vector<T> pi; ///< (n) probability that item k is cached anywhere
52 std::vector<T> pi0; ///< (n) miss probability, 1 - pi
53 Matrix<T> pij; ///< (n x h) probability that item k sits in list l
54 std::vector<T> x; ///< (h) per-list throughput
55 Matrix<T> u; ///< (n x h) utilization, x(l) gamma(k,l)
56 T E; ///< always 1: see the reference defect noted above
57};
58
59/**
60 * @brief Exact mean value analysis of a multi-list cache.
61 *
62 * @param gamma (n x h) access factors
63 * @param m (h) list capacities
64 */
65template <class T>
66CacheMvaResult<T> cache_mva(const Matrix<T>& gamma, const std::vector<int>& m) {
67 const std::size_t n = gamma.rows();
68 const std::size_t h = gamma.cols();
69 if (h != m.size()) throw InputError("cache_mva: gamma and m disagree on the number of lists");
70 for (int v : m)
71 if (v < 0) throw InputError("cache_mva: negative capacity");
72
73 // Mixed-radix enumeration of the occupancy box, list 0 varying fastest.
74 std::vector<std::size_t> stride(h, 1);
75 std::size_t total = 1;
76 for (std::size_t l = 0; l < h; ++l) {
77 stride[l] = total;
78 total *= static_cast<std::size_t>(m[l]) + 1;
79 }
80
81 const T zero = num_traits<T>::from_int(0);
82 const T one = num_traits<T>::from_int(1);
83
84 // pi(s,k) over the whole box; pij only needs the current and final states,
85 // but is kept per-state to mirror the reference exactly.
86 Matrix<T> pi(total, n, zero);
87 std::vector<Matrix<T>> pij(total, Matrix<T>(n, h, zero));
88 std::vector<T> x(h, zero);
89
90 std::vector<int> mcur(h, 0);
91 for (std::size_t s = 0; s < total; ++s) {
92 for (std::size_t l = 0; l < h; ++l) {
93 if (mcur[l] == 0) continue; // m - e_l is outside the box
94 const std::size_t s_l = s - stride[l];
95 T den = zero;
96 for (std::size_t k = 0; k < n; ++k) den += gamma(k, l) * (one - pi(s_l, k));
97 if (den == zero)
98 throw NumericError(
99 "cache_mva: list has zero aggregate access factor, the throughput is undefined");
100 x[l] = num_traits<T>::from_int(static_cast<long>(mcur[l])) / den;
101 for (std::size_t k = 0; k < n; ++k) {
102 const T p = gamma(k, l) * (one - pi(s_l, k)) * x[l];
103 pij[s](k, l) = p;
104 pi(s, k) += p;
105 }
106 }
107 // advance the mixed-radix counter
108 for (std::size_t l = 0; l < h; ++l) {
109 if (++mcur[l] <= m[l]) break;
110 mcur[l] = 0;
111 }
112 }
113
114 const std::size_t sfin = total - 1; // the state equal to m
116 r.pi.assign(n, zero);
117 r.pi0.assign(n, zero);
118 for (std::size_t k = 0; k < n; ++k) {
119 r.pi[k] = pi(sfin, k);
120 r.pi0[k] = one - r.pi[k];
121 }
122 r.pij = pij[sfin];
123 r.x = x;
124 r.u = Matrix<T>(n, h, zero);
125 for (std::size_t l = 0; l < h; ++l)
126 for (std::size_t k = 0; k < n; ++k) r.u(k, l) = x[l] * gamma(k, l);
127 r.E = one;
128 return r;
129}
130
131} // namespace cache
132} // namespace line
133
134#endif // LINE_API_CACHE_MVA_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense matrix and non-owning view.
CacheMvaResult< T > cache_mva(const Matrix< T > &gamma, const std::vector< int > &m)
Exact mean value analysis of a multi-list cache.
Definition cache_mva.h:66
Number-type abstraction for the templated API port.
Return value of cache_mva, mirroring [pi,pi0,pij,x,u,E].
Definition cache_mva.h:50
std::vector< T > pi0
(n) miss probability, 1 - pi
Definition cache_mva.h:52
Matrix< T > pij
(n x h) probability that item k sits in list l
Definition cache_mva.h:53
std::vector< T > pi
(n) probability that item k is cached anywhere
Definition cache_mva.h:51
Matrix< T > u
(n x h) utilization, x(l) gamma(k,l)
Definition cache_mva.h:55
std::vector< T > x
(h) per-list throughput
Definition cache_mva.h:54
T E
always 1: see the reference defect noted above
Definition cache_mva.h:56