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);
31 % estimate call response times at hostlayers
32 self.callservt = zeros(lqn.ncalls,1);
33 self.callresidt = zeros(lqn.ncalls,1);
34 for r=1:size(self.call_classes_updmap,1)
35 idx = self.call_classes_updmap(r,1);
36 cidx = self.call_classes_updmap(r,2);
37 nodeidx = self.call_classes_updmap(r,3);
38 classidx = self.call_classes_updmap(r,4);
39 if self.call_classes_updmap(r,3) > 1
41 self.callservt(cidx) = 0;
42 self.callresidt(cidx) = 0;
44 % Include call multiplicity in callservt (matching updateMetricsDefault)
45 self.callservt(cidx) = self.results{end, self.idxhash(idx)}.RN(nodeidx,classidx) * self.lqn.callproc{cidx}.getMean;
46 % callresidt uses WN which already includes visit multiplicity
47 self.callresidt(cidx) = self.results{end, self.idxhash(idx)}.WN(nodeidx,classidx);
52 % then resolve the entry servt summing up these contributions
53 entry_servt = (eye(lqn.nidx+lqn.ncalls)-self.servtmatrix)\[self.servt;self.callservt];
54 entry_servt(1:lqn.eshift) = 0;
56 % Propagate forwarding calls: add target entry
's service time to source entry
57 for cidx = 1:lqn.ncalls
58 if lqn.calltype(cidx) == CallType.FWD
59 source_eidx = lqn.callpair(cidx, 1);
60 target_eidx = lqn.callpair(cidx, 2);
61 fwd_prob = lqn.callproc{cidx}.getMean();
62 entry_servt(source_eidx) = entry_servt(source_eidx) + fwd_prob * entry_servt(target_eidx);
66 self.servt(lqn.eshift+1:lqn.eshift+lqn.nentries) = entry_servt(lqn.eshift+1:lqn.eshift+lqn.nentries);
67 entry_servt((lqn.ashift+1):end) = 0;
69 % Compute entry-level residt using servtmatrix and activity residt
70 % callresidt uses WN which already includes visit multiplicity
71 entry_residt = self.servtmatrix*[self.residt;self.callresidt(:)];
72 entry_residt(1:lqn.eshift) = 0;
73 % Scale entry residt by task/entry throughput ratio (matching updateMetricsDefault)
74 for eidx=(lqn.eshift+1):(lqn.eshift+lqn.nentries)
75 tidx = lqn.parent(eidx);
76 hidx = lqn.parent(tidx);
77 if ~self.ignore(tidx) && ~self.ignore(hidx)
78 hasSyncCallers = full(any(lqn.issynccaller(:, eidx)));
80 tidxclass = ensemble{self.idxhash(hidx)}.attribute.tasks(find(ensemble{self.idxhash(hidx)}.attribute.tasks(:,2) == tidx),1);
81 eidxclass = ensemble{self.idxhash(hidx)}.attribute.entries(find(ensemble{self.idxhash(hidx)}.attribute.entries(:,2) == eidx),1);
82 task_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,tidxclass));
83 entry_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,eidxclass));
84 self.servt(eidx) = entry_servt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
85 self.residt(eidx) = entry_residt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
87 self.residt(eidx) = entry_residt(eidx);
92 for r=1:size(self.call_classes_updmap,1)
93 cidx = self.call_classes_updmap(r,2);
94 eidx = lqn.callpair(cidx,2);
95 if self.call_classes_updmap(r,3) > 1
96 self.servtproc{eidx} = Exp.fitMean(self.servt(eidx));
100 % determine call response times processes
101 for r=1:size(self.call_classes_updmap,1)
102 cidx = self.call_classes_updmap(r,2);
103 eidx = lqn.callpair(cidx,2);
104 if self.call_classes_updmap(r,3) > 1
106 % note that respt is per visit, so number of calls is 1
107 self.callservt(cidx) = self.servt(eidx);
108 self.callservtproc{cidx} = self.servtproc{eidx};
110 % note that respt is per visit, so number of calls is 1
111 self.callservtproc{cidx} = Exp.fitMean(self.callservt(cidx));
116 self.servtcdf = cell(lqn.nidx,1);
119 % first obtain servt of activities at hostlayers
120 self.servt = zeros(lqn.nidx,1);
121 self.residt = zeros(lqn.nidx,1);
122 for r=1:size(self.servt_classes_updmap,1)
123 idx = self.servt_classes_updmap(r,1);
124 aidx = self.servt_classes_updmap(r,2);
125 nodeidx = self.servt_classes_updmap(r,3);
126 classidx = self.servt_classes_updmap(r,4);
127 self.tput(aidx) = self.results{end,self.idxhash(idx)}.TN(nodeidx,classidx);
129 % Compute residt from QN/TN_ref (matching updateMetricsDefault)
130 layerIdx = self.idxhash(idx);
131 layerSn = ensemble{layerIdx}.getStruct();
132 c = find(layerSn.chains(:, classidx), 1);
133 refclass_c = layerSn.refclass(c);
134 refstat_k = layerSn.refstat(classidx);
135 TN_ref = self.results{end,layerIdx}.TN(refstat_k, refclass_c);
136 if TN_ref > GlobalConstants.FineTol
137 self.residt(aidx) = self.results{end,layerIdx}.QN(nodeidx,classidx) / TN_ref;
139 self.residt(aidx) = self.results{end,layerIdx}.WN(nodeidx,classidx);
142 submodelidx = self.idxhash(idx);
143 if submodelidx>length(repo)
144 % Try SolverFluid first for actual response time CDFs with
145 % higher-moment information; fall back to layer solver's
146 % exponential approximation
if Fluid fails on the layer model
148 repo{submodelidx} = SolverFluid(ensemble{submodelidx}).getCdfRespT;
150 repo{submodelidx} = self.solvers{submodelidx}.getCdfRespT;
153 self.servtcdf{aidx} = repo{submodelidx}{nodeidx,classidx};
156 self.callservtcdf = cell(lqn.ncalls,1);
158 % estimate call response times at hostlayers
159 self.callservt = zeros(lqn.ncalls,1);
160 self.callresidt = zeros(lqn.ncalls,1);
161 for r=1:size(self.call_classes_updmap,1)
162 idx = self.call_classes_updmap(r,1);
163 cidx = self.call_classes_updmap(r,2);
164 nodeidx = self.call_classes_updmap(r,3);
165 classidx = self.call_classes_updmap(r,4);
166 if self.call_classes_updmap(r,3) > 1
167 submodelidx = self.idxhash(idx);
168 if submodelidx>length(repo)
170 repo{submodelidx} = SolverFluid(ensemble{submodelidx}).getCdfRespT;
172 repo{submodelidx} = self.solvers{submodelidx}.getCdfRespT;
176 self.callservtcdf{cidx} = repo{submodelidx}{nodeidx,classidx};
178 self.callservtcdf{cidx} = repo{submodelidx};
180 % Also set callresidt from WN (includes visit multiplicity)
182 self.callresidt(cidx) = self.results{end, self.idxhash(idx)}.WN(nodeidx,classidx);
186 cdf = [self.servtcdf;self.callservtcdf];
188 % then resolve the entry servt summing up these contributions
189 matrix = inv((eye(lqn.nidx+lqn.ncalls)-self.servtmatrix));
190 for i = 1:1:lqn.nentries
192 convolidx = find(matrix(eidx,:)>0);
193 convolidx(find(convolidx<=lqn.eshift+lqn.nentries))=[];
196 while num<length(convolidx)
197 fitidx = convolidx(num+1);
198 [m1,m2,m3,~,~] = EmpiricalCDF(cdf{fitidx}).getMoments;
199 % Use CoarseTol to skip near-zero mean CDFs (e.g., Immediate activities)
200 % whose APH fitting produces extreme rate parameters that cause
201 % matrix exponential evaluation to hang
202 if m1>GlobalConstants.CoarseTol
203 fitdist = APH.fitRawMoments(m1,m2,m3);
204 aphparam{1} = fitdist.params{1}.paramValue; % alpha
205 aphparam{2} = fitdist.params{2}.paramValue; % T
207 % For call indices, multiply repetitions by mean number of calls
208 % servtmatrix has 1.0
for calls, but we need callproc.getMean() repetitions
209 reps = matrix(eidx,fitidx);
211 cidx_local = fitidx - lqn.nidx;
212 reps = reps * lqn.callproc{cidx_local}.getMean();
214 integerRepetitions = floor(reps);
215 fractionalPartRepetitions = reps - integerRepetitions;
217 if fractionalPartRepetitions == 0
218 ParamCell = [ParamCell,repmat(aphparam,1,integerRepetitions)];
219 elseif integerRepetitions>0 && fractionalPartRepetitions>0
220 ParamCell = [ParamCell,repmat(aphparam,1,integerRepetitions)];
221 zerodist = APH.fitMeanAndSCV(GlobalConstants.FineTol,0.99);
222 zeroparam{1} = zerodist.params{1}.paramValue;
223 zeroparam{2} = zerodist.params{2}.paramValue;
224 aph_pattern = 3; % branch structure
225 [aphparam{1},aphparam{2}] = aph_simplify(aphparam{1},aphparam{2},zeroparam{1},zeroparam{2},fractionalPartRepetitions,1-fractionalPartRepetitions, aph_pattern);
226 ParamCell = [ParamCell,aphparam];
228 zerodist = APH.fitMeanAndSCV(GlobalConstants.FineTol,0.99);
229 zeroparam{1} = zerodist.params{1}.paramValue;
230 zeroparam{2} = zerodist.params{2}.paramValue;
231 aph_pattern = 3; % branch structure
232 [aphparam{1},aphparam{2}] = aph_simplify(aphparam{1},aphparam{2},zeroparam{1},zeroparam{2},fractionalPartRepetitions,1-fractionalPartRepetitions, aph_pattern);
233 ParamCell = [ParamCell,aphparam];
235 if fitidx <= lqn.nidx
236 self.servtproc{fitidx} = Exp.fitMean(m1);
237 self.servt(fitidx) = m1;
239 self.callservtproc{fitidx-lqn.nidx} = Exp.fitMean(m1);
240 self.callservt(fitidx-lqn.nidx) = m1;
245 if isempty(ParamCell)
246 self.servt(eidx) = 0;
248 [alpha,T] = aph_convseq(ParamCell); % convolution of sequential activities
249 entry_dist = APH(alpha,T);
250 self.entryproc{eidx-(lqn.nhosts+lqn.ntasks)} = entry_dist;
251 self.servt(eidx) = entry_dist.getMean;
252 self.servtproc{eidx} = Exp.fitMean(self.servt(eidx));
253 self.entrycdfrespt{eidx-(lqn.nhosts+lqn.ntasks)} = entry_dist.evalCDF;
257 % Propagate forwarding calls: add target entry
's service time to source entry
258 for cidx = 1:lqn.ncalls
259 if lqn.calltype(cidx) == CallType.FWD
260 source_eidx = lqn.callpair(cidx, 1);
261 target_eidx = lqn.callpair(cidx, 2);
262 fwd_prob = lqn.callproc{cidx}.getMean();
263 self.servt(source_eidx) = self.servt(source_eidx) + fwd_prob * self.servt(target_eidx);
264 self.servtproc{source_eidx} = Exp.fitMean(self.servt(source_eidx));
268 % Compute entry-level residt using servtmatrix and activity residt
269 % callresidt uses WN which already includes visit multiplicity
270 entry_residt = self.servtmatrix*[self.residt;self.callresidt(:)];
271 entry_residt(1:lqn.eshift) = 0;
272 for eidx=(lqn.eshift+1):(lqn.eshift+lqn.nentries)
273 tidx = lqn.parent(eidx);
274 hidx = lqn.parent(tidx);
275 if ~self.ignore(tidx) && ~self.ignore(hidx)
276 hasSyncCallers = full(any(lqn.issynccaller(:, eidx)));
278 tidxclass = ensemble{self.idxhash(hidx)}.attribute.tasks(find(ensemble{self.idxhash(hidx)}.attribute.tasks(:,2) == tidx),1);
279 eidxclass = ensemble{self.idxhash(hidx)}.attribute.entries(find(ensemble{self.idxhash(hidx)}.attribute.entries(:,2) == eidx),1);
280 task_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,tidxclass));
281 entry_tput = sum(self.results{end,self.idxhash(hidx)}.TN(ensemble{self.idxhash(hidx)}.attribute.clientIdx,eidxclass));
282 self.residt(eidx) = entry_residt(eidx) * task_tput / max(GlobalConstants.Zero, entry_tput);
284 self.residt(eidx) = entry_residt(eidx);
289 % determine call response times processes
290 for r=1:size(self.call_classes_updmap,1)
291 cidx = self.call_classes_updmap(r,2);
292 eidx = lqn.callpair(cidx,2);
293 if self.call_classes_updmap(r,3) > 1
295 self.callservt(cidx) = self.servt(eidx);
296 self.callservtproc{cidx} = Exp.fitMean(self.servt(eidx));
301self.ensemble = ensemble;