LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
da_cacheqn_retrieval.m
1function [res, hitprob, missprob, delayedprob, it, sn] = da_cacheqn_retrieval(sn, netfun, options)
2% [RES,HITPROB,MISSPROB,DELAYEDPROB,IT,SN] = DA_CACHEQN_RETRIEVAL(SN, NETFUN, OPTIONS)
3%
4% Decomposition-aggregation driver for a CLOSED integrated cache-queueing model
5% whose Cache node has a delayed-hit retrieval system (Cache.setRetrievalSystem).
6% Adapts da_cacheqn: the cache is relabelled as a ClassSwitch (read -> hit / miss)
7% and the finite-population delayed-hit coalescing is made to emerge from the
8% closed AMVA by giving the retrieval (fetch) station a load-dependent COALESCING
9% service rate. With k would-be-miss jobs at the fetch station spanning
10% d(k) = n_eff*(1-(1-1/n_eff)^k) distinct uncached items (n_eff = nitems -
11% totalcapacity), the single fetch server releases a whole coalesced batch per
12% fetch, so the job-completion rate is mu(k) = (1/F)*k/d(k), i.e.
13% lldscaling(fetch,k) = k/d(k). The distinct-fetch (backend) throughput then
14% saturates at 1/F and the delayed-hit fraction = would-be-miss - fetcher/X
15% emerges from the finite population, rather than from an open-arrival closed form.
16%
17% Returns the netsolve result RES, and per-cache hitprob/missprob/delayedprob
18% (1 x nclasses on the read class), the iteration count IT and the mutated SN.
19%
20% LIMITATIONS (FURTHER WORK NEEDED - EXPERIMENTAL):
21% - hitprob/missprob are the true cache probabilities (hit = P(item cached),
22% miss = 1 - hit) and are accurate (uniform: exact m/n; validated vs LDES).
23% delayedprob is returned as 0: the finite-population delayed-hit FRACTION is
24% NOT recovered analytically (it folds into miss). Deriving the exact
25% closed-population coalescing split remains an open problem.
26% - The coalescing THROUGHPUT benefit is captured only in DIRECTION and is
27% UNDERSTATED: vs LDES it recovers the right sign of the throughput gain but a
28% smaller magnitude (e.g. LN ~+21% where LDES shows ~+31%). The load-dependent
29% lldscaling(k)=k/d(k) closure is calibrated for small closed populations (the
30% LN sublayer regime) and drifts for large N.
31% - Absolute LCQ throughput may carry the pre-existing LN(MVA) approximation
32% error for backend-bottleneck models (present with or without retrieval).
33% - Not yet ported to JAR/Python; single fetch station (single-backend) only.
34%
35% Copyright (c) 2012-2026, Imperial College London
36% All rights reserved.
37
38I = sn.nnodes;
39K = sn.nclasses;
40
41statefulNodes = find(sn.isstateful)';
42statefulNodesClasses = [];
43for ind = statefulNodes
44 statefulNodesClasses(end+1:end+K) = ((ind-1)*K+1):(ind*K);
45end
46
47caches = find(sn.nodetype == NodeType.Cache);
48if numel(caches) ~= 1
49 line_error(mfilename, 'da_cacheqn_retrieval requires exactly one Cache node.');
50end
51ci = caches(1);
52ch = sn.nodeparam{ci};
53
54% --- retrieval configuration ---
55rk = keys(ch.retrievalSystemQueueIndices);
56readClass = double(rk{1}) + 1; % 1-indexed read class
57queueNodes = double(ch.retrievalSystemQueueIndices(rk{1}));
58if numel(queueNodes) ~= 1
59 line_error(mfilename, 'da_cacheqn_retrieval currently supports a single-station (single-backend) retrieval system.');
60end
61fetchNode = queueNodes(1);
62fetchStation = sn.nodeToStation(fetchNode);
63nitems = ch.nitems;
64totcap = sum(ch.itemcap);
65n_eff = max(1, nitems - totcap); % distinct uncacheable items
66
67% closed population
68Npop = round(sum(sn.njobs(~isinf(sn.njobs))));
69
70% mean fetch service F of the read class at the fetch station (per-item identical here)
71rcls0 = ch.retrievalClasses(1, readClass);
72F = 1 / sn.rates(fetchStation, rcls0);
73
74% --- load-dependent COALESCING rate on the fetch station: lldscaling(k)=k/d(k) ---
75alpha = ones(1, max(1, Npop));
76for k = 1:Npop
77 d = n_eff * (1 - (1 - 1/n_eff)^k);
78 alpha(k) = k / d;
79end
80if isempty(sn.lldscaling)
81 sn.lldscaling = ones(sn.nstations, Npop);
82elseif size(sn.lldscaling,2) < Npop
83 sn.lldscaling(:, end+1:Npop) = 1;
84end
85sn.lldscaling(fetchStation, 1:Npop) = alpha;
86
87% relabel the cache as a class switch
88sn.nodetype(ci) = NodeType.ClassSwitch;
89
90hitClass = ch.hitclass;
91missClass = ch.missclass;
92retrievalClasses = ch.retrievalClasses; % (item x class) -> retrieval class
93pread = ch.pread{readClass};
94pread = pread(:).' / sum(pread);
95
96% --- fixed point on the read-class cache arrival rate ---
97hitprob = zeros(1, K);
98missprob = zeros(1, K);
99delayedprob = zeros(1, K);
100res = struct();
101
102lambda0 = zeros(1, K);
103lambda0(readClass) = 1; % seed
104fpopts = options;
105fpopts.config.da_norm = @(d) norm(d, 1);
106[~, it] = da_fpi(@da_sweep, lambda0, fpopts);
107
108 function [xnew, xref] = da_sweep(x, itnum) %#ok<INUSD>
109 lambda = x;
110
111 % isolated cache occupancy -> per-item uncached (would-be-miss) prob pi0_i
112 [gamma, lambda_cache, ~] = da_cache_isolate(ch, lambda);
113 [~, ~, ~, pi0] = cache_miss_fpi(gamma, ch.itemcap, lambda_cache); % 1 x nitems
114 pi0 = reshape(pi0, 1, []);
115 nonhit = sum(pread .* pi0); % aggregate would-be-miss prob
116 hp = 1 - nonhit;
117
118 % Rebuild the cache class-switch routing (the flat rtnodes cannot express the
119 % state-dependent cache/retrieval routing, so we wire it explicitly):
120 % read at cache -> hit class (self-loop; exits via the existing
121 % hit -> CS -> Think/read chain) with prob hp;
122 % read at cache -> per-item retrieval class AT the fetch station with prob
123 % pread_i*pi0_i (item-weighted would-be-miss);
124 % retrieval class at the fetch station -> miss class at the cache (fetch
125 % complete; exits via the existing miss -> CS -> Think/read
126 % chain). This breaks the flat Cache<->Retr loop so a fetch
127 % is a single visit.
128 r = readClass;
129 sn.rtnodes((ci-1)*K+r, :) = 0;
130 sn.rtnodes((ci-1)*K+r, (ci-1)*K+hitClass(r)) = hp;
131 for i = 1:nitems
132 rcls = retrievalClasses(i, r);
133 if rcls > 0
134 sn.rtnodes((ci-1)*K+r, (fetchNode-1)*K+rcls) = pread(i) * pi0(i);
135 sn.rtnodes((fetchNode-1)*K+rcls, :) = 0;
136 sn.rtnodes((fetchNode-1)*K+rcls, (ci-1)*K+missClass(r)) = 1;
137 sn.rtnodes((ci-1)*K+rcls, :) = 0; % drop the unused Cache->Retr edge
138 end
139 end
140 sn.rt = dtmc_stochcomp(sn.rtnodes, statefulNodesClasses);
141
142 [visits, nodevisits, sn] = sn_refresh_visits(sn, sn.chains, sn.rt, sn.rtnodes);
143 sn.visits = visits;
144 sn.nodevisits = nodevisits;
145
146 res = netfun(sn);
147
148 % throughput -> new read arrival rate at the cache
149 nv = cellsum(nodevisits);
150 c = find(sn.chains(:, r));
151 inchain = find(sn.chains(c, :));
152 refnode = sn.stationToNode(sn.refstat(r));
153 if sn.refclass(c) > 0
154 denom = nv(refnode, sn.refclass(c));
155 else
156 denom = nv(refnode, r);
157 end
158 Xr = sum(res.XN(inchain));
159 lambda(r) = Xr * nv(ci, r) / denom;
160
161 % Cache hit / miss probabilities. hit = P(item cached) from the isolated
162 % cache occupancy at the converged read rate; miss = 1 - hit is the
163 % would-be-miss probability. The retrieval system's coalescing is captured in
164 % the closed AMVA throughput (via the load-dependent fetch station), which
165 % sets the correct read rate lambda and hence the correct occupancy; the
166 % delayed-hit fraction is not separated here (it folds into miss).
167 hitprob(r) = hp;
168 missprob(r) = nonhit;
169 delayedprob(r) = 0;
170
171 xnew = lambda;
172 xref = x;
173 end
174end
Definition Station.m:245