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)
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.
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.
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.
35% Copyright (c) 2012-2026, Imperial College London
41statefulNodes = find(sn.isstateful)
';
42statefulNodesClasses = [];
43for ind = statefulNodes
44 statefulNodesClasses(end+1:end+K) = ((ind-1)*K+1):(ind*K);
47caches = find(sn.nodetype == NodeType.Cache);
49 line_error(mfilename, 'da_cacheqn_retrieval
requires exactly one Cache node.
');
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.
');
61fetchNode = queueNodes(1);
62fetchStation = sn.nodeToStation(fetchNode);
64totcap = sum(ch.itemcap);
65n_eff = max(1, nitems - totcap); % distinct uncacheable items
68Npop = round(sum(sn.njobs(~isinf(sn.njobs))));
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);
74% --- load-dependent COALESCING rate on the fetch station: lldscaling(k)=k/d(k) ---
75alpha = ones(1, max(1, Npop));
77 d = n_eff * (1 - (1 - 1/n_eff)^k);
80if isempty(sn.lldscaling)
81 sn.lldscaling = ones(sn.nstations, Npop);
82elseif size(sn.lldscaling,2) < Npop
83 sn.lldscaling(:, end+1:Npop) = 1;
85sn.lldscaling(fetchStation, 1:Npop) = alpha;
87% relabel the cache as a class switch
88sn.nodetype(ci) = NodeType.ClassSwitch;
90hitClass = ch.hitclass;
91missClass = ch.missclass;
92retrievalClasses = ch.retrievalClasses; % (item x class) -> retrieval class
93pread = ch.pread{readClass};
94pread = pread(:).' / sum(pread);
96% --- fixed point on
the read-
class cache arrival rate ---
98missprob = zeros(1, K);
99delayedprob = zeros(1, K);
102lambda0 = zeros(1, K);
103lambda0(readClass) = 1; % seed
105fpopts.config.da_norm = @(d) norm(d, 1);
106[~, it] = da_fpi(@da_sweep, lambda0, fpopts);
108 function [xnew, xref] = da_sweep(x, itnum) %#ok<INUSD>
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
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
129 sn.rtnodes((ci-1)*K+r, :) = 0;
130 sn.rtnodes((ci-1)*K+r, (ci-1)*K+hitClass(r)) = hp;
132 rcls = retrievalClasses(i, r);
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
140 sn.rt = dtmc_stochcomp(sn.rtnodes, statefulNodesClasses);
142 [
visits,
nodevisits, sn] = sn_refresh_visits(sn, sn.chains, sn.rt, sn.rtnodes);
148 % throughput ->
new read arrival rate at
the cache
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));
156 denom = nv(refnode, r);
158 Xr = sum(res.XN(inchain));
159 lambda(r) = Xr * nv(ci, r) / denom;
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).
168 missprob(r) = nonhit;