1%{ @file cache_miss_rmf.m
2 % @brief Computes miss rates
using the refined mean field (RMF) method
4 % @author LINE Development Team
8 % @brief Computes cache miss rates
for RANDOM(m) replacement via refined mean field
11 % This function computes global, per-user, and per-item miss rates
for
12 % multi-list caches with RANDOM(m) replacement
using the DDPP mean field
13 % approximation with 1/N correction (refined mean field). The cache
14 % occupancy
is computed from the aggregate request stream; per-user miss
15 % rates weight the per-item miss probabilities by each user
's rates.
18 % N. Gast, "Expected Values Estimated via Mean-Field Approximation are
19 % 1/N-Accurate", Proc. ACM Meas. Anal. Comput. Syst., 2017.
23 % [M, MU, MI, pi0] = cache_miss_rmf(gamma, m, lambda)
28 % <tr><th>Name<th>Description
29 % <tr><td>gamma<td>Item access factors (used for sizing only)
30 % <tr><td>m<td>Cache capacity vector
31 % <tr><td>lambda<td>Arrival rates per user per item per list
36 % <tr><th>Name<th>Description
37 % <tr><td>M<td>Global miss rate
38 % <tr><td>MU<td>Per-user miss rate
39 % <tr><td>MI<td>Per-item miss rate
40 % <tr><td>pi0<td>Per-item miss probability
43function [M,MU,MI,pi0,tout,pi0_t,MU_t,xtraj] = cache_miss_rmf(gamma, m, lambda, tspan, x0init, accost) %#ok<INUSL>
44% Optional TSPAN = [t0, t1] and initial occupancy X0INIT request the
45% transient mean-field trajectory: integrating the same drift F(x) that
46% RMF_FIXED_POINT drives to steady state, over the finite window. Returns
47% the time grid TOUT, the per-item list-0 occupancy PI0_T (n_items x nt)
48% and the per-user miss-rate trajectory MU_T (u x nt). Omitting TSPAN
49% preserves the original steady-state-only contract.
51% Optional ACCOST is the per-(user,item) access graph (each an (h+1)x(h+1)
52% matrix): row 1 is miss admission (col 1 = reject, col 1+l = admit to list l),
53% row 1+i is a hit in list i (col 1+b = promote to list b>=i). A non-linear
54% graph modulates admission/promotion per item; the drift then follows the
55% general RANDOM(m) dynamics (RMF_DRIFT_GRAPH) with the plain mean-field fixed
56% point, while the standard linear chain keeps the 1/N-refined path unchanged.
57if nargin < 4, tspan = []; end
58if nargin < 5, x0init = []; end
59if nargin < 6, accost = []; end
61n_items = size(lambda,2);
65% aggregate per-item request rates over users
66lam_i = zeros(1, n_items);
69 row(~isfinite(row)) = 0;
72p = lam_i / sum(lam_i);
74model_dim = n_items * (h + 1);
76% Build initial state: first m(1) items in list 1, next m(2) in
77% list 2, etc.; remaining items outside cache (list 0)
78x0 = zeros(model_dim, 1);
82 obj_idx = obj_idx + 1;
84 x0(rmf_index(obj_idx, k, n_items)) = 1.0;
88for i = (obj_idx + 1):n_items
89 x0(rmf_index(i, 0, n_items)) = 1.0;
92% see _kb/09-ldes-and-cache.md (mean-field cache fixed points and TTL edge cases)
93% A non-default access graph (accost) modulates admission (row 1) and promotion
94% (row 1+i) per item; the general drift honours it with the plain mean-field
95% fixed point, while the linear chain keeps the 1/N-refined path.
96G = rmf_build_item_graphs(accost, lambda, n_items, h);
98 xss = rmf_fixed_point_graph(x0, p, G, m, n_items, h, model_dim);
100 xss = rmf_fixed_point(x0, p, m, n_items, h, model_dim);
101 % The refinement's reduced linear system
is expected to be singular for
102 % small/non-hyperbolic fixed points; silence only that specific warning here
103 % since the non-finite result
is detected and rejected below.
104 ws1 = warning('off', 'MATLAB:singularMatrix');
105 ws2 = warning('off', 'MATLAB:nearlySingularMatrix');
106 restoreWarn = onCleanup(@() warning([ws1 ws2]));
108 [pi_mf, V] = rmf_expansion_steady_state(x0, p, m, n_items, h, model_dim);
109 xref = pi_mf + V / n_items;
110 if all(isfinite(xref))
114 % Fall back to plain mean field
119% Per-item miss probability (occupancy of list 0), clipped to [0,1]
120pi0 = zeros(n_items, 1);
122 pi0(i) = max(0, min(1, xss(rmf_index(i, 0, n_items))));
129 row(~isfinite(row)) = 0;
134% Transient mean-field trajectory (optional): integrate the drift over the
135% requested window from the supplied (or default) initial occupancy. XTRAJ
is
136% the full DDPP occupancy trajectory (model_dim x nt), used to carry the cache
137% mean occupancy across environment switches.
138tout = []; pi0_t = []; MU_t = []; xtraj = [];
145 ode_func = @(t, x) rmf_drift(x, p, m, n_items, h, model_dim);
146 odeopt = odeset('AbsTol', 1e-10, 'RelTol', 1e-8);
147 [tout, xtraj] = ode15s(ode_func, tspan, x0init, odeopt);
148 xtraj = xtraj'; % model_dim x nt
150 pi0_t = zeros(n_items, nt);
152 pi0_t(i,:) = max(0, min(1, xtraj(rmf_index(i, 0, n_items), :)));
157 row(~isfinite(row)) = 0;
158 MU_t(v,:) = row * pi0_t;
163function idx = rmf_index(i, k, n_items)
164% RMF_INDEX Map (item i, list k) to flat state index
165% i: item index (1-based)
166% k: list index (0 = outside cache, 1..h = cache lists)
167% n_items: total number of items
168idx = i + k * n_items;
171function hr = rmf_hit_rate(x, p, list_number, n_items)
172% RMF_HIT_RATE Compute hit rate contribution from a specific list
173% hr = sum_i p(i) * x(index(i, list_number))
176 hr = hr + p(i) * x(rmf_index(i, list_number, n_items));
180function dX = rmf_drift(x, p, m, n_items, h, model_dim)
181% RMF_DRIFT Compute mean field drift F(x) for RANDOM(m) replacement
183% dx[i,k]/dt = -p(i)*x[i,k] + hitRate[k]*x[i,k+1]/m(k) (promotion from k to k+1)
184% dx[i,k+1]/dt = p(i)*x[i,k] - hitRate[k]*x[i,k+1]/m(k)
186hit_rates = zeros(1, h + 1);
188 hit_rates(k + 1) = rmf_hit_rate(x, p, k, n_items);
191dX = zeros(model_dim, 1);
194 flow = p(i) * x(rmf_index(i, k, n_items)) ...
195 - hit_rates(k + 1) * x(rmf_index(i, k + 1, n_items)) / m(k + 1);
196 dX(rmf_index(i, k, n_items)) = dX(rmf_index(i, k, n_items)) - flow;
197 dX(rmf_index(i, k + 1, n_items)) = dX(rmf_index(i, k + 1, n_items)) + flow;
202function Fp = rmf_jacobian(x, p, m, n_items, h, model_dim)
203% RMF_JACOBIAN Compute Jacobian dF/dx at state x
205hit_rates = zeros(1, h + 1);
207 hit_rates(k + 1) = rmf_hit_rate(x, p, k, n_items);
210Fp = zeros(model_dim, model_dim);
213 ik = rmf_index(i, k, n_items);
214 ik1 = rmf_index(i, k + 1, n_items);
217 Fp(ik, ik) = Fp(ik, ik) - p(i);
218 Fp(ik1, ik) = Fp(ik1, ik) + p(i);
219 Fp(ik, ik1) = Fp(ik, ik1) + hit_rates(k + 1) / m(k + 1);
220 Fp(ik1, ik1) = Fp(ik1, ik1) - hit_rates(k + 1) / m(k + 1);
222 % Indirect terms via hit rate dependence on x(j,k)
224 jk = rmf_index(j, k, n_items);
225 jk1 = rmf_index(j, k + 1, n_items);
226 Fp(ik, jk1) = Fp(ik, jk1) - p(i) * x(ik) / m(k + 1);
227 Fp(ik1, jk1) = Fp(ik1, jk1) + p(i) * x(ik) / m(k + 1);
228 Fp(ik, jk) = Fp(ik, jk) + p(j) * x(ik1) / m(k + 1);
229 Fp(ik1, jk) = Fp(ik1, jk) - p(j) * x(ik1) / m(k + 1);
235function Fpp = rmf_hessian(~, p, m, n_items, h, model_dim)
236% RMF_HESSIAN Compute Hessian d^2F/dx^2 (constant for this quadratic drift)
238Fpp = zeros(model_dim, model_dim, model_dim);
241 ik = rmf_index(i, k, n_items);
242 ik1 = rmf_index(i, k + 1, n_items);
245 jk = rmf_index(j, k, n_items);
246 jk1 = rmf_index(j, k + 1, n_items);
247 % d^2 F[ik] / (d x[jk] d x[ik1])
248 Fpp(ik, jk, ik1) = Fpp(ik, jk, ik1) + p(j) / m(k + 1);
249 Fpp(ik, ik1, jk) = Fpp(ik, ik1, jk) + p(j) / m(k + 1);
250 % d^2 F[ik] / (d x[jk1] d x[ik])
251 Fpp(ik, jk1, ik) = Fpp(ik, jk1, ik) - p(i) / m(k + 1);
252 Fpp(ik, ik, jk1) = Fpp(ik, ik, jk1) - p(i) / m(k + 1);
254 Fpp(ik1, jk, ik1) = Fpp(ik1, jk, ik1) - p(j) / m(k + 1);
255 Fpp(ik1, ik1, jk) = Fpp(ik1, ik1, jk) - p(j) / m(k + 1);
256 Fpp(ik1, jk1, ik) = Fpp(ik1, jk1, ik) + p(i) / m(k + 1);
257 Fpp(ik1, ik, jk1) = Fpp(ik1, ik, jk1) + p(i) / m(k + 1);
264function Q = rmf_noise_matrix(x, p, m, n_items, h, model_dim)
265% RMF_NOISE_MATRIX Compute noise intensity matrix Q(x) for the DDPP
267% Q[a,b] = sum_ell ell[a]*ell[b]*beta_ell(x)
268% Each transition swaps items i and j across lists k and k+1.
270Q = zeros(model_dim, model_dim);
271signs = [-1, 1, 1, -1];
275 rate = p(i) * x(rmf_index(i, k, n_items)) ...
276 * x(rmf_index(j, k + 1, n_items)) / m(k + 1);
277 indices = [rmf_index(i, k, n_items), ...
278 rmf_index(j, k, n_items), ...
279 rmf_index(i, k + 1, n_items), ...
280 rmf_index(j, k + 1, n_items)];
283 Q(indices(ia), indices(ib)) = Q(indices(ia), indices(ib)) ...
284 + rate * signs(ia) * signs(ib);
292function pi = rmf_fixed_point(x0, p, m, n_items, h, model_dim)
293% RMF_FIXED_POINT Compute mean field fixed point by ODE integration
295% Integrates dx/dt = F(x) until steady state using ode15s.
298ode_func = @(t, x) rmf_drift(x, p, m, n_items, h, model_dim);
299odeopt = odeset('AbsTol', 1e-10, 'RelTol', 1e-8);
300[~, xvec] = ode15s(ode_func, [0, tmax], x0, odeopt);
301%[~, xvec] = lsoda_solve(ode_func, [0, tmax], x0, odeopt);
305function [C, Cinv, rk] = rmf_dimension_reduction(Fp, n_items, h, model_dim)
306% RMF_DIMENSION_REDUCTION Compute change-of-basis for singular Jacobian
308% The Jacobian
is singular because item populations are conserved
309% (sum over lists for each item = 1). Returns matrices to project
310% onto the non-singular subspace.
314C = zeros(model_dim, model_dim);
317 for i = 1:(n_items - 1)
319 C(d, rmf_index(i, l_idx, n_items)) = 1.0;
324C((rk + 1):model_dim, :) = U(:, (rk + 1):model_dim)';
328function [pi, V] = rmf_expansion_steady_state(x0, p, m, n_items, h, model_dim)
329% RMF_EXPANSION_STEADY_STATE Compute refined mean field steady-state expansion
331% Computes the mean field fixed point pi and the 1/N correction V
332% using the Lyapunov equation approach with dimension reduction.
334% The refined approximation for a system of N items
is:
335% E[X] ~ pi + V/N + O(1/N^2)
337pi = rmf_fixed_point(x0, p, m, n_items, h, model_dim);
339Fp = rmf_jacobian(pi, p, m, n_items, h, model_dim);
340Fpp = rmf_hessian(pi, p, m, n_items, h, model_dim);
341Q = rmf_noise_matrix(pi, p, m, n_items, h, model_dim);
343% Dimension reduction: project onto non-singular subspace
344[C, Cinv, rk] = rmf_dimension_reduction(Fp, n_items, h, model_dim);
346Fp_r = (C * Fp * Cinv);
347Fp_r = Fp_r(1:rk, 1:rk);
349% Reduce Hessian: Fpp_r(a,b,c) =
sum_{i,j,k} C(a,i)*Fpp(i,j,k)*Cinv(j,b)*Cinv(k,c)
350% First contraction: tmp1(a,j,k) = sum_i C(a,i)*Fpp(i,j,k)
351tmp1 = zeros(model_dim, model_dim, model_dim);
355 tmp1(a, j, k) = C(a, :) * Fpp(:, j, k);
359% Second contraction: tmp2(a,b,k) = sum_j tmp1(a,j,k)*Cinv(j,b)
360tmp2 = zeros(rk, rk, model_dim);
364 tmp2(a, b, k) = tmp1(a, :, k) * Cinv(:, b);
368% Third contraction: Fpp_r(a,b,c) = sum_k tmp2(a,b,k)*Cinv(k,c)
369Fpp_r = zeros(rk, rk, rk);
372 Fpp_r(a, b, :) = reshape(tmp2(a, b, :), 1, []) * Cinv(:, 1:rk);
377Q_r = Q_r(1:rk, 1:rk);
379% Solve Lyapunov equation: Fp_r * W_r + W_r * Fp_r' + Q_r = 0
380W_r = lyap(Fp_r, Q_r);
382% First-order correction: V_r = -Fp_r \ (C_r / 2)
383% where C_r =
sum_{b,c} Fpp_r(:,b,c) * W_r(b,c)
388 C_r(a) = C_r(a) + Fpp_r(a, b, c) * W_r(b, c);
392V_r = -Fp_r \ (C_r / 2.0);
394% Expand back to full dimension
395V = Cinv(:, 1:rk) * V_r;
398function g = rmf_linear_graph(h)
399% RMF_LINEAR_GRAPH Standard linear chain: miss->list1, hit in list a->list a+1,
400% self-loop on the top list. (h+1)x(h+1), 1-based (col/row 1 = out/reject).
409function G = rmf_build_item_graphs(accost, lambda, n, h)
410% RMF_BUILD_ITEM_GRAPHS Per-item (h+1)x(h+1) access graph aggregated over users
411% by request rate. Returns {} when accost
is absent or the standard linear
412% chain (so the caller keeps the refined linear path). ACCOST
is cell{v,k}.
417lin = rmf_linear_graph(h);
422 num = zeros(h+1, h+1); den = 0;
424 wv = sum(lambda(v, k, 1));
425 if ~isfinite(wv), wv = 0; end
427 if isempty(gvk),
continue; end
428 num = num + wv * gvk;
435 if isempty(gk), gk = lin; end
438 srow = sum(gk(a, :));
439 if srow > 0, gk(a, :) = gk(a, :) / srow; end
442 if ~all(all(abs(gk - lin) < 1e-9))
451function dX = rmf_drift_graph(x, p, G, m, n, h, model_dim)
452% RMF_DRIFT_GRAPH General RANDOM(m) mean-field drift honouring per-item access
453% graph G{k} (h+1)x(h+1). Miss admission weighted by row 1, hit promotion by
454% row 1+i; a uniformly random occupant of the target list
is displaced (evicted
455% out on a miss, swapped to the source list on a hit), matching the exact RR
456% sample path (State.afterEventCache). Reduces to the linear eq-8 drift when G
457%
is the standard chain.
458x = max(0, min(1, x));
459A = zeros(h+1, h+1); % A(s+1, i): insertion/promotion into list i from source s
462 xjs = x(rmf_index(j, s, n));
463 if xjs == 0,
continue; end
466 A(s+1, i) = A(s+1, i) + p(j) * xjs * gj(s+1, i+1);
470dX = zeros(model_dim, 1);
472 outk = x(rmf_index(k, 0, n));
475 xki = x(rmf_index(k, i, n));
476 infl = p(k) * outk * gk(1, i+1);
478 infl = infl + p(k) * x(rmf_index(k, s, n)) * gk(s+1, i+1);
481 infl = infl + A(i+1, b) * x(rmf_index(k, b, n)) / m(b);
483 outfl = p(k) * xki * (1 - gk(i+1, i+1));
486 disp = disp + A(s+1, i);
488 outfl = outfl + disp * xki / m(i);
489 dX(rmf_index(k, i, n)) = dX(rmf_index(k, i, n)) + infl - outfl;
493 acc = acc + dX(rmf_index(k, i, n));
495 dX(rmf_index(k, 0, n)) = -acc;
499function pj = rmf_fixed_point_graph(x0, p, G, m, n, h, model_dim)
500% RMF_FIXED_POINT_GRAPH Plain mean-field fixed point of the general drift.
502ode_func = @(t, x) rmf_drift_graph(x, p, G, m, n, h, model_dim);
503odeopt = odeset(
'AbsTol', 1e-10,
'RelTol', 1e-8);
504[~, xvec] = ode15s(ode_func, [0, tmax], x0, odeopt);