LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mva_cacheqn_analyzer.m
1function [Q,U,R,T,C,X,lG,hitprob,missprob,runtime,it] = solver_mva_cacheqn_analyzer(self, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER] = SOLVER_MVA_CACHEQN_ANALYZER(SELF, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7snorig = self.model.getStruct;
8sn = snorig;
9I = sn.nnodes;
10K = sn.nclasses;
11
12line_debug('MVA cacheqn analyzer starting: method=%s, nclasses=%d', options.method, K);
13
14statefulNodes = find(sn.isstateful)';
15statefulNodesClasses = [];
16for ind=statefulNodes %#ok<FXSET>
17 statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
18end
19lambda = zeros(1,K);
20lambda_1 = zeros(1,K);
21caches = find(sn.nodetype == NodeType.Cache);
22
23hitprob = zeros(length(caches),K);
24missprob = zeros(length(caches),K);
25
26% per-cache converged inputs, used to report the per-item occupancy through
27% getAvgItemTable via the strategy-appropriate (scalable) kernel
28cacheGamma = cell(1,length(caches));
29cacheM = cell(1,length(caches));
30cacheLambda = cell(1,length(caches));
31cacheRcost = cell(1,length(caches));
32cacheStrat = cell(1,length(caches));
33
34for it=1:options.iter_max
35 for ind=caches
36 ch = sn.nodeparam{ind};
37 hitClass = ch.hitclass;
38 missClass = ch.missclass;
39 inputClass = find(hitClass);
40 m = ch.itemcap;
41 n = ch.nitems;
42 if it == 1
43 % initial random value of arrival rates to the cache
44 lambda_1(inputClass) = rand(1,length(inputClass));
45 lambda = lambda_1;
46 sn.nodetype(ind) = NodeType.ClassSwitch;
47 end
48
49 % solution of isolated cache
50 h = length(m);
51 u = length(lambda);
52 lambda_cache = zeros(u,n,h);
53
54 for v=1:u
55 for k=1:n
56 for l=1:(h+1)
57 if ~isnan(ch.pread{v})
58 lambda_cache(v,k,l) = lambda(v) * ch.pread{v}(k);
59 end
60 end
61 end
62 end
63
64 Rcost = ch.accost;
65 if isempty(Rcost)
66 % Default linear cache routing: items flow from list l to list l+1
67 Rcost = cell(u, n);
68 for v = 1:u
69 for k = 1:n
70 Rmat = diag(ones(1, h), 1);
71 Rmat(h+1, h+1) = 1;
72 Rcost{v, k} = Rmat;
73 end
74 end
75 end
76 gamma = cache_gamma_lp(lambda_cache,Rcost);
77 cacheGamma{caches==ind} = gamma;
78 cacheM{caches==ind} = m;
79 cacheLambda{caches==ind} = lambda_cache;
80 cacheRcost{caches==ind} = Rcost;
81 cacheStrat{caches==ind} = ch.replacestrat;
82
83 switch options.method
84 case 'exact'
85 [~,~,pij] = cache_mva(gamma, m);
86 pij = [abs(1-sum(pij,2)),pij];
87 missrate(ind,:) = zeros(1,u);
88 for v=1:u
89 missrate(ind,v) = lambda_cache(v,:,1)*pij(:,1);
90 end
91 otherwise
92 line_debug('Default method: using FPI approximation for cache\n');
93 line_debug('Using FPI approximation, calling cache_miss_fpi');
94 [~,missrate(ind,:)] = cache_miss_fpi(gamma, m, lambda_cache);
95 end
96 missprob(ind,:) = missrate(ind,:) ./ lambda; % we set to NaN if no arrivals
97 hitprob(ind,:) = 1 - missprob(ind,:);
98 hitprob(isnan(hitprob)) = 0;
99 missprob(isnan(missprob)) = 0;
100
101 % bring back the isolated model results into the queueing model
102 for r=inputClass
103 sn.rtnodes((ind-1)*K+r,:) = 0;
104 for jnd=1:I
105 if sn.connmatrix(ind,jnd)
106 sn.rtnodes((ind-1)*K+r,(jnd-1)*K+hitClass(r)) = hitprob(ind,r);
107 sn.rtnodes((ind-1)*K+r,(jnd-1)*K+missClass(r)) = missprob(ind,r);
108 end
109 end
110 end
111 sn.rt = dtmc_stochcomp(sn.rtnodes,statefulNodesClasses);
112 end
113 [visits, nodevisits, sn] = sn_refresh_visits(sn, sn.chains, sn.rt, sn.rtnodes);
114 sn.visits = visits;
115 sn.nodevisits = nodevisits;
116
117 switch options.method
118 case {'aba.upper', 'aba.lower', 'bjb.upper', 'bjb.lower', 'pb.upper', 'pb.lower', 'gb.upper', 'gb.lower', 'sb.upper', 'sb.lower'}
119 [Q,U,R,T,C,X,lG,runtime] = solver_mva_bound_analyzer(sn, options);
120 otherwise
121 if ~isempty(sn.lldscaling) || ~isempty(sn.cdscaling)
122 [Q,U,R,T,C,X,lG,runtime] = solver_mvald_analyzer(sn, options);
123 else
124 [Q,U,R,T,C,X,lG,runtime] = solver_mva_analyzer(sn, options);
125 end
126
127 nodevisits = cellsum(nodevisits);
128 for ind=caches
129 for r=inputClass
130 c = find(sn.chains(:,r));
131 inchain = find(sn.chains(c,:));
132 if sn.refclass(c)>0
133 lambda(r) = sum(X(inchain)) * nodevisits(ind,r) / nodevisits(sn.stationToNode(sn.refstat(r)),sn.refclass(c));
134 else
135 lambda(r) = sum(X(inchain)) * nodevisits(ind,r) / nodevisits(sn.stationToNode(sn.refstat(r)),r);
136 end
137 end
138 end
139 if norm(lambda-lambda_1,1) < options.iter_tol
140 break
141 end
142 lambda_1 = lambda;
143end
144
145% Report the per-item occupancy [nitems x (lists+1)] (col 1 = miss) from the
146% converged access factors, so getAvgItemTable is populated for integrated
147% cache-queueing models too. LRU uses the scalable TTL kernel; RR/FIFO use the
148% exact product-form recursion, which is only tractable for small item sets and
149% is therefore skipped (NaN, with a warning) when there are more than 10 items.
150for ci = 1:length(caches)
151 if ~isempty(cacheGamma{ci})
152 ni = size(cacheGamma{ci},1);
153 hi = numel(cacheM{ci});
154 if cacheStrat{ci} == ReplacementStrategy.LRU
155 itemprob = cache_ttl_lrua(cacheLambda{ci}, cacheRcost{ci}, cacheM{ci});
156 elseif ni > 10
157 line_warning(mfilename, 'Per-item cache occupancy (getAvgItemTable) requires the exact kernel for RR/FIFO and is skipped for caches with more than 10 items (%d items); reporting NaN.', ni);
158 itemprob = NaN(ni, hi+1);
159 else
160 itemprob = cache_prob_erec(cacheGamma{ci}, cacheM{ci});
161 end
162 self.model.nodes{caches(ci)}.setResultItemProb(itemprob);
163 end
164end
165end