1function [QN,UN,RN,TN,CN,XN,lG,pij,runtime,method,hitproblist,itemprob] = solver_nc_cache_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,PIJ,RUNTIME,METHOD,HITPROBLIST] = SOLVER_NC_CACHE_ANALYZER(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
11XN = zeros(1,sn.nclasses);
15line_debug(
'NC cache analyzer starting: method=%s, nclasses=%d', options.method, sn.nclasses);
17source_ist = sn.nodeToStation(sn.nodetype == NodeType.Source);
18sourceRate = sn.rates(source_ist,:);
19sourceRate(isnan(sourceRate)) = 0;
20TN(source_ist,:) = sourceRate;
22ch = sn.nodeparam{sn.nodetype == NodeType.Cache};
28 line_error(mfilename,
'NC requires the number of items to exceed the cache capacity at least by 2.');
38 if ~isnan(ch.pread{v})
39 lambda(v,k,l) = sourceRate(v) * ch.pread{v}(k);
47 % Default linear cache routing: items flow from list l to list l+1
51 Rmat = diag(ones(1, h), 1);
57gamma = cache_gamma_lp(lambda,R);
58% per-list hit probabilities are genuine only on the exact branch; see
59% _kb/09-ldes-and-cache.md on cache-analyzer per-list/per-item reporting
60pijlist = []; % genuine per-list occupancy (n x h), set in the exact branch
63 % cache_prob_erec
is exact only
for the exchangeable (RR/FIFO/RANDOM)
64 % family; see _kb/09-ldes-and-cache.md on cache-analyzer reporting
65 switch sn.nodeparam{sn.nodetype == NodeType.Cache}.replacestrat
66 case {ReplacementStrategy.RR, ReplacementStrategy.FIFO}
69 line_error(mfilename,
'NC does not support exact solution of the specified cache replacement policy; use the default (approximate) method or SolverCTMC.');
71 line_debug(
'Using exact method, calling cache_prob_erec');
72 [pij] = cache_prob_erec(gamma, m);
73 missRate = zeros(1,u);
75 missRate(v) = lambda(v,:,1)*pij(:,1);
77 pijlist = pij(:, 2:end);
80 line_debug(
'Using sampling method, calling cache_miss_is');
81 [~,missRate,~,~,lE] = cache_miss_is(gamma, m, lambda, options.samples);
82 pij = cache_prob_is(gamma, m, options.samples);
85 line_debug(
'Default method: using SPM approximation method\n');
86 line_debug(
'Using SPM approximation method, calling cache_miss_spm');
87 [~,missRate,~,~,lE] = cache_miss_spm(gamma, m, lambda);
88 pij = cache_prob_spm(gamma, m, lE);
93 if length(ch.hitclass)>=r && ch.missclass(r)>0 && ch.hitclass(r)>0
94 XN(ch.missclass(r)) = XN(ch.missclass(r)) + missRate(r);
95 XN(ch.hitclass(r)) = XN(ch.hitclass(r)) + (sourceRate(r) - missRate(r));
99% per-list (per-level) hit probabilities (access-weighted), only where the
100% exact algorithm produced a genuine per-list occupancy matrix.
101hitproblist = NaN(u, h);
104 if any(~isnan(ch.pread{v}))
105 pread = ch.pread{v}(:).
';
107 hitproblist(v,l) = pread * pijlist(:,l);
112% per-item occupancy [nitems x (lists+1)] (col 1 = miss) from the exact
113% cache_prob_erec recursion (skipped, NaN, for >10 items); see
114% _kb/09-ldes-and-cache.md on cache-analyzer per-list/per-item reporting
116 line_warning(mfilename, 'Per-item cache occupancy (getAvgItemTable)
requires the exact algorithm and
is skipped
for caches with more than 10 items (%d items); reporting NaN.
', n);
117 itemprob = NaN(n, h+1);
118elseif ~isempty(pijlist)
119 itemprob = pij; % exact branch: already [n x (h+1)] with col 1 = miss
121 itemprob = cache_prob_erec(gamma, m); % [n x (h+1)] with col 1 = miss