1function updateMetricsMomentBased(self,it)
2ensemble = self.ensemble;
5 % first obtain servt of activities at hostlayers
6 self.servt = zeros(lqn.nidx,1);
7 self.residt = zeros(lqn.nidx,1);
8 for r=1:size(self.servt_classes_updmap,1)
9 idx = self.servt_classes_updmap(r,1);
10 aidx = self.servt_classes_updmap(r,2);
11 nodeidx = self.servt_classes_updmap(r,3);
12 classidx = self.servt_classes_updmap(r,4);
13 self.servt(aidx) = self.results{end,self.idxhash(idx)}.RN(nodeidx,classidx);
14 self.tput(aidx) = self.results{end,self.idxhash(idx)}.TN(nodeidx,classidx);
15 self.servtproc{aidx} = Exp.fitMean(self.servt(aidx));
17 % Compute residt from QN/TN_ref (matching updateMetricsDefault)
18 layerIdx = self.idxhash(idx);
19 layerSn = ensemble{layerIdx}.getStruct();
20 c = find(layerSn.chains(:, classidx), 1);
21 refclass_c = layerSn.refclass(c);
22 refstat_k = layerSn.refstat(classidx);
23 TN_ref = self.results{end,layerIdx}.TN(refstat_k, refclass_c);
24 if TN_ref > GlobalConstants.FineTol
25 self.residt(aidx) = self.results{end,layerIdx}.QN(nodeidx,classidx) / TN_ref;
27 self.residt(aidx) = self.results{end,layerIdx}.WN(nodeidx,classidx);
30 % see _kb/06-solver-catalog.md (LN section)
for rationale
31 zt_act = lqn_act_thinktime(lqn, aidx);
33 self.servt(aidx) = self.servt(aidx) + zt_act;
34 self.residt(aidx) = self.residt(aidx) + zt_act;
35 self.servtproc{aidx} = Exp.fitMean(self.servt(aidx));
39 % estimate call response times at hostlayers
40 self.callservt = zeros(lqn.ncalls,1);
41 self.callresidt = zeros(lqn.ncalls,1);
42 for r=1:size(self.call_classes_updmap,1)
43 idx = self.call_classes_updmap(r,1);
44 cidx = self.call_classes_updmap(r,2);
45 nodeidx = self.call_classes_updmap(r,3);
46 classidx = self.call_classes_updmap(r,4);
47 if self.call_classes_updmap(r,3) > 1
49 self.callservt(cidx) = 0;
50 self.callresidt(cidx) = 0;
52 % Include call multiplicity in callservt (matching updateMetricsDefault)
53 self.callservt(cidx) = self.results{end, self.idxhash(idx)}.RN(nodeidx,classidx) * self.lqn.callproc{cidx}.getMean;
54 % callresidt uses WN which already includes visit multiplicity
55 self.callresidt(cidx) = self.results{end, self.idxhash(idx)}.WN(nodeidx,classidx);
60 % then resolve the entry servt summing up these contributions
61 entry_servt = (eye(lqn.nidx+lqn.ncalls)-self.servtmatrix)\[self.servt;self.callservt];
62 entry_servt(1:lqn.eshift) = 0;
64 % Propagate forwarding calls: add target entry
's service time to source entry
65 for cidx = 1:lqn.ncalls
66 if lqn.calltype(cidx) == CallType.FWD
67 source_eidx = lqn.callpair(cidx, 1);
68 target_eidx = lqn.callpair(cidx, 2);
69 fwd_prob = lqn.callproc{cidx}.getMean();
70 entry_servt(source_eidx) = entry_servt(source_eidx) + fwd_prob * entry_servt(target_eidx);
74 self.servt(lqn.eshift+1:lqn.eshift+lqn.nentries) = entry_servt(lqn.eshift+1:lqn.eshift+lqn.nentries);
75 entry_servt((lqn.ashift+1):end) = 0;
77 % Compute entry-level residt using servtmatrix and activity residt
78 % callresidt uses WN which already includes visit multiplicity
79 entry_residt = self.servtmatrix*[self.residt;self.callresidt(:)];
80 entry_residt(1:lqn.eshift) = 0;
81 % Scale entry residt by task/entry throughput ratio (matching updateMetricsDefault)
82 for eidx=(lqn.eshift+1):(lqn.eshift+lqn.nentries)
83 tidx = lqn.parent(eidx);
84 hidx = lqn.parent(tidx);
85 if ~self.ignore(tidx) && ~self.ignore(hidx)
86 hasSyncCallers = full(any(lqn.issynccaller(:, eidx)));
88 tidxclass = ensemble{self.idxhash(hidx)}.attribute.tasks(find(ensemble{self.idxhash(hidx)}.attribute.tasks(:,2) == tidx),1);
89 eidxclass = ensemble{self.idxhash(hidx)}.attribute.entries(find(ensemble{self.idxhash(hidx)}.attribute.entries(:,2) == eidx),1);
90 task_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,tidxclass));
91 entry_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,eidxclass));
92 self.servt(eidx) = entry_servt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
93 self.residt(eidx) = entry_residt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
95 self.residt(eidx) = entry_residt(eidx);
100 for r=1:size(self.call_classes_updmap,1)
101 cidx = self.call_classes_updmap(r,2);
102 eidx = lqn.callpair(cidx,2);
103 if self.call_classes_updmap(r,3) > 1
104 self.servtproc{eidx} = Exp.fitMean(self.servt(eidx));
108 % determine call response times processes
109 for r=1:size(self.call_classes_updmap,1)
110 cidx = self.call_classes_updmap(r,2);
111 eidx = lqn.callpair(cidx,2);
112 if self.call_classes_updmap(r,3) > 1
114 % note that respt is per visit, so number of calls is 1
115 self.callservt(cidx) = self.servt(eidx);
116 self.callservtproc{cidx} = self.servtproc{eidx};
118 % note that respt is per visit, so number of calls is 1
119 self.callservtproc{cidx} = Exp.fitMean(self.callservt(cidx));
124 self.servtcdf = cell(lqn.nidx,1);
127 % first obtain servt of activities at hostlayers
128 self.servt = zeros(lqn.nidx,1);
129 self.residt = zeros(lqn.nidx,1);
130 for r=1:size(self.servt_classes_updmap,1)
131 idx = self.servt_classes_updmap(r,1);
132 aidx = self.servt_classes_updmap(r,2);
133 nodeidx = self.servt_classes_updmap(r,3);
134 classidx = self.servt_classes_updmap(r,4);
135 self.tput(aidx) = self.results{end,self.idxhash(idx)}.TN(nodeidx,classidx);
137 % Compute residt from QN/TN_ref (matching updateMetricsDefault)
138 layerIdx = self.idxhash(idx);
139 layerSn = ensemble{layerIdx}.getStruct();
140 c = find(layerSn.chains(:, classidx), 1);
141 refclass_c = layerSn.refclass(c);
142 refstat_k = layerSn.refstat(classidx);
143 TN_ref = self.results{end,layerIdx}.TN(refstat_k, refclass_c);
144 if TN_ref > GlobalConstants.FineTol
145 self.residt(aidx) = self.results{end,layerIdx}.QN(nodeidx,classidx) / TN_ref;
147 self.residt(aidx) = self.results{end,layerIdx}.WN(nodeidx,classidx);
150 submodelidx = self.idxhash(idx);
151 if submodelidx>length(repo)
152 % see _kb/06-solver-catalog.md (LN section) for rationale
154 repo{submodelidx} = SolverFluid(ensemble{submodelidx}).getCdfRespT;
156 repo{submodelidx} = self.solvers{submodelidx}.getCdfRespT;
159 self.servtcdf{aidx} = repo{submodelidx}{nodeidx,classidx};
162 self.callservtcdf = cell(lqn.ncalls,1);
164 % estimate call response times at hostlayers
165 self.callservt = zeros(lqn.ncalls,1);
166 self.callresidt = zeros(lqn.ncalls,1);
167 for r=1:size(self.call_classes_updmap,1)
168 idx = self.call_classes_updmap(r,1);
169 cidx = self.call_classes_updmap(r,2);
170 nodeidx = self.call_classes_updmap(r,3);
171 classidx = self.call_classes_updmap(r,4);
172 if self.call_classes_updmap(r,3) > 1
173 submodelidx = self.idxhash(idx);
174 if submodelidx>length(repo)
176 repo{submodelidx} = SolverFluid(ensemble{submodelidx}).getCdfRespT;
178 repo{submodelidx} = self.solvers{submodelidx}.getCdfRespT;
182 self.callservtcdf{cidx} = repo{submodelidx}{nodeidx,classidx};
184 self.callservtcdf{cidx} = repo{submodelidx};
186 % Also set callresidt from WN (includes visit multiplicity)
188 self.callresidt(cidx) = self.results{end, self.idxhash(idx)}.WN(nodeidx,classidx);
192 cdf = [self.servtcdf;self.callservtcdf];
194 % then resolve the entry servt summing up these contributions
195 matrix = inv((eye(lqn.nidx+lqn.ncalls)-self.servtmatrix));
196 for i = 1:1:lqn.nentries
198 convolidx = find(matrix(eidx,:)>0);
199 convolidx(find(convolidx<=lqn.eshift+lqn.nentries))=[];
202 while num<length(convolidx)
203 fitidx = convolidx(num+1);
204 [m1,m2,m3,~,~] = EmpiricalCDF(cdf{fitidx}).getMoments;
206 % see _kb/06-solver-catalog.md (LN section) for rationale
207 if fitidx <= lqn.nidx && lqn_act_thinktime(lqn, fitidx) > 0
208 ztd = lqn.actthink{fitidx};
210 sig2 = ztd.getSCV()*t1^2;
212 t3 = ztd.getSkewness()*sig2^1.5 + 3*t1*t2 - 2*t1^3;
213 m3 = m3 + 3*m2*t1 + 3*m1*t2 + t3;
214 m2 = m2 + 2*m1*t1 + t2;
217 % Use CoarseTol to skip near-zero mean CDFs (e.g., Immediate activities)
218 % whose APH fitting produces extreme rate parameters that cause
219 % matrix exponential evaluation to hang
220 if m1>GlobalConstants.CoarseTol
221 fitdist = APH.fitRawMoments(m1,m2,m3);
222 aphparam{1} = fitdist.params{1}.paramValue; % alpha
223 aphparam{2} = fitdist.params{2}.paramValue; % T
225 % For call indices, multiply repetitions by mean number of calls
226 % servtmatrix has 1.0 for calls, but we need callproc.getMean() repetitions
227 reps = matrix(eidx,fitidx);
229 cidx_local = fitidx - lqn.nidx;
230 reps = reps * lqn.callproc{cidx_local}.getMean();
232 integerRepetitions = floor(reps);
233 fractionalPartRepetitions = reps - integerRepetitions;
235 if fractionalPartRepetitions == 0
236 ParamCell = [ParamCell,repmat(aphparam,1,integerRepetitions)];
237 elseif integerRepetitions>0 && fractionalPartRepetitions>0
238 ParamCell = [ParamCell,repmat(aphparam,1,integerRepetitions)];
239 % see _kb/06-solver-catalog.md (LN section) for rationale
240 zerodist = APH(1, -GlobalConstants.Immediate);
241 zeroparam{1} = zerodist.params{1}.paramValue;
242 zeroparam{2} = zerodist.params{2}.paramValue;
243 aph_pattern = 3; % branch structure
244 [aphparam{1},aphparam{2}] = aph_simplify(aphparam{1},aphparam{2},zeroparam{1},zeroparam{2},fractionalPartRepetitions,1-fractionalPartRepetitions, aph_pattern);
245 ParamCell = [ParamCell,aphparam];
247 % see _kb/06-solver-catalog.md (LN section) for rationale
248 zerodist = APH(1, -GlobalConstants.Immediate);
249 zeroparam{1} = zerodist.params{1}.paramValue;
250 zeroparam{2} = zerodist.params{2}.paramValue;
251 aph_pattern = 3; % branch structure
252 [aphparam{1},aphparam{2}] = aph_simplify(aphparam{1},aphparam{2},zeroparam{1},zeroparam{2},fractionalPartRepetitions,1-fractionalPartRepetitions, aph_pattern);
253 ParamCell = [ParamCell,aphparam];
255 if fitidx <= lqn.nidx
256 self.servtproc{fitidx} = Exp.fitMean(m1);
257 self.servt(fitidx) = m1;
259 self.callservtproc{fitidx-lqn.nidx} = Exp.fitMean(m1);
260 self.callservt(fitidx-lqn.nidx) = m1;
265 if isempty(ParamCell)
266 self.servt(eidx) = 0;
268 [alpha,T] = aph_convseq(ParamCell); % convolution of sequential activities
269 entry_dist = APH(alpha,T);
270 self.entryproc{eidx-(lqn.nhosts+lqn.ntasks)} = entry_dist;
271 self.servt(eidx) = entry_dist.getMean;
272 self.servtproc{eidx} = Exp.fitMean(self.servt(eidx));
273 self.entrycdfrespt{eidx-(lqn.nhosts+lqn.ntasks)} = entry_dist.evalCDF;
277 % Propagate forwarding calls: add target entry's service time to source entry
278 for cidx = 1:lqn.ncalls
279 if lqn.calltype(cidx) == CallType.FWD
280 source_eidx = lqn.callpair(cidx, 1);
281 target_eidx = lqn.callpair(cidx, 2);
282 fwd_prob = lqn.callproc{cidx}.getMean();
283 self.servt(source_eidx) = self.servt(source_eidx) + fwd_prob * self.servt(target_eidx);
284 self.servtproc{source_eidx} = Exp.fitMean(self.servt(source_eidx));
288 % Compute entry-level residt
using servtmatrix and activity residt
289 % callresidt uses WN which already includes visit multiplicity
290 entry_residt = self.servtmatrix*[self.residt;self.callresidt(:)];
291 entry_residt(1:lqn.eshift) = 0;
292 for eidx=(lqn.eshift+1):(lqn.eshift+lqn.nentries)
293 tidx = lqn.parent(eidx);
294 hidx = lqn.parent(tidx);
295 if ~self.ignore(tidx) && ~self.ignore(hidx)
296 hasSyncCallers = full(any(lqn.issynccaller(:, eidx)));
298 tidxclass = ensemble{self.idxhash(hidx)}.attribute.tasks(find(ensemble{self.idxhash(hidx)}.attribute.tasks(:,2) == tidx),1);
299 eidxclass = ensemble{self.idxhash(hidx)}.attribute.entries(find(ensemble{self.idxhash(hidx)}.attribute.entries(:,2) == eidx),1);
300 task_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,tidxclass));
301 entry_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,eidxclass));
302 self.residt(eidx) = entry_residt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
304 self.residt(eidx) = entry_residt(eidx);
309 % determine call response times processes
310 for r=1:size(self.call_classes_updmap,1)
311 cidx = self.call_classes_updmap(r,2);
312 eidx = lqn.callpair(cidx,2);
313 if self.call_classes_updmap(r,3) > 1
315 self.callservt(cidx) = self.servt(eidx);
316 self.callservtproc{cidx} = Exp.fitMean(self.servt(eidx));
321self.ensemble = ensemble;