LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getEnsembleAvg.m
1function [QN,UN,RN,TN,AN,WN] = getEnsembleAvg(self)
2% [QN,UN,RN,TN,AN,WN] = GETENSEMBLEAVG(SELF)
3
4% Check if solver was properly constructed (may have returned early due to unsupported features)
5if isempty(self.ensemble)
6 QN = []; UN = []; RN = []; TN = []; AN = []; WN = [];
7 return;
8end
9
10% Show library attribution if verbose and not yet shown
11if self.options.verbose ~= VerboseLevel.SILENT && ~GlobalConstants.isLibraryAttributionShown()
12 libs = SolverLN.getLibrariesUsed([], self.options);
13 if ~isempty(libs)
14 line_printf('The solver will leverage %s.\n', strjoin(libs, ', '));
15 GlobalConstants.setLibraryAttributionShown(true);
16 end
17end
18
19iterate(self); % run iterations
20QN = nan(self.lqn.nidx,1);
21UN = nan(self.lqn.nidx,1);
22RN = nan(self.lqn.nidx,1);
23TN = nan(self.lqn.nidx,1);
24PN = nan(self.lqn.nidx,1); % utilization will be first stored here
25SN = nan(self.lqn.nidx,1); % response time will be first stored here
26WN = nan(self.lqn.nidx,1); % residence time
27AN = nan(self.lqn.nidx,1); % not available yet
28WN_processed = false(self.lqn.nidx,1); % track activities already accumulated into task WN
29E = self.nlayers;
30for e=1:E
31 clientIdx = self.ensemble{e}.attribute.clientIdx;
32 serverIdx = self.ensemble{e}.attribute.serverIdx;
33 sourceIdx = self.ensemble{e}.attribute.sourceIdx;
34 % determine processor metrics
35 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
36 hidx = self.ensemble{e}.stations{serverIdx}.attribute.idx;
37 TN(hidx) = 0;
38 PN(hidx) = 0;
39 for c=1:self.ensemble{e}.getNumberOfClasses
40 if self.ensemble{e}.classes{c}.completes
41 t = 0;
42 u = 0;
43 if ~isnan(clientIdx)
44 t = max(t, self.results{end,e}.TN(clientIdx,c));
45 end
46 if ~isnan(sourceIdx)
47 t = max(t, self.results{end,e}.TN(sourceIdx,c));
48 end
49 TN(hidx) = TN(hidx) + max(t,self.results{end,e}.TN(serverIdx,c));
50 end
51 type = self.ensemble{e}.classes{c}.attribute(1);
52 switch type
53 case LayeredNetworkElement.ACTIVITY
54 aidx = self.ensemble{e}.classes{c}.attribute(2);
55 tidx = self.lqn.parent(aidx);
56 if isnan(PN(aidx)), PN(aidx)=0; end
57 if isnan(PN(tidx)), PN(tidx)=0; end
58 PN(aidx) = PN(aidx) + self.results{end,e}.UN(serverIdx,c);
59 PN(tidx) = PN(tidx) + self.results{end,e}.UN(serverIdx,c);
60 PN(hidx) = PN(hidx) + self.results{end,e}.UN(serverIdx,c);
61 end
62 end
63 TN(hidx) = NaN; % added for consistency with LQNS
64 end
65
66 % determine remaining metrics
67 for c=1:self.ensemble{e}.getNumberOfClasses
68 type = self.ensemble{e}.classes{c}.attribute(1);
69 switch type
70 case LayeredNetworkElement.TASK
71 tidx = self.ensemble{e}.classes{c}.attribute(2);
72 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
73 if isnan(TN(tidx))
74 % store the result in the processor
75 % model
76 TN(tidx) = self.results{end,e}.TN(clientIdx,c);
77 end
78 else
79 % nop
80 end
81 case LayeredNetworkElement.ENTRY
82 eidx = self.ensemble{e}.classes{c}.attribute(2);
83 tidx = self.lqn.parent(eidx);
84 % For phase-2 models, use residt (caller's view with overtaking)
85 % Otherwise use servt (total service time = response time)
86 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
87 SN(eidx) = self.residt(eidx); % Phase-1 + overtaking correction
88 else
89 SN(eidx) = self.servt(eidx);
90 end
91 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
92 if isnan(TN(eidx))
93 % store the result in the processor model
94 if isnan(TN(eidx)), TN(eidx)=0; end
95 TN(eidx) = self.results{end,e}.TN(clientIdx,c);
96 end
97 else
98 % nop
99 end
100 case LayeredNetworkElement.CALL
101 cidx = self.ensemble{e}.classes{c}.attribute(2);
102 aidx = self.lqn.callpair(cidx,1);
103 % Only sync calls contribute to caller's response time
104 if self.lqn.calltype(cidx) == CallType.SYNC
105 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c) * self.lqn.callproc{cidx}.getMean();
106 end
107 if isnan(QN(aidx)), QN(aidx)=0; end
108 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
109 case LayeredNetworkElement.ACTIVITY
110 aidx = self.ensemble{e}.classes{c}.attribute(2);
111 tidx = self.lqn.parent(aidx);
112 if isnan(QN(tidx)), QN(tidx)=0; end
113 QN(tidx) = QN(tidx) + self.results{end,e}.QN(serverIdx,c);
114 if isnan(TN(aidx)), TN(aidx)=0; end
115 if isnan(QN(aidx)), QN(aidx)=0; end
116
117 % For forwarding targets: propagate activity metrics to entry and task
118 % Check if task has its own class (non-forwarding targets do)
119 hasTaskClass = any(self.ensemble{e}.attribute.tasks(:,2) == tidx);
120 if ~hasTaskClass
121 % Propagate activity throughput to task for forwarding targets
122 if isnan(TN(tidx)), TN(tidx)=0; end
123 switch self.ensemble{e}.classes{c}.type
124 case JobClassType.CLOSED
125 TN(tidx) = TN(tidx) + self.results{end,e}.TN(serverIdx,c);
126 case JobClassType.OPEN
127 TN(tidx) = TN(tidx) + self.results{end,e}.TN(sourceIdx,c);
128 end
129 end
130
131 % Find the entry this activity is bound to
132 for eidx_check = self.lqn.entriesof{tidx}
133 if self.lqn.graph(eidx_check, aidx) > 0 % Activity is bound to this entry
134 if isnan(TN(eidx_check)), TN(eidx_check)=0; end
135 if isnan(QN(eidx_check)), QN(eidx_check)=0; end
136 if isnan(SN(eidx_check)), SN(eidx_check)=0; end
137 % Add activity metrics to entry (for entries without their own classes)
138 actTput = 0;
139 switch self.ensemble{e}.classes{c}.type
140 case JobClassType.CLOSED
141 actTput = self.results{end,e}.TN(serverIdx,c);
142 case JobClassType.OPEN
143 actTput = self.results{end,e}.TN(sourceIdx,c);
144 end
145 % Only add if entry doesn't have its own class (forwarding target case)
146 hasEntryClass = any(self.ensemble{e}.attribute.entries(:,2) == eidx_check);
147 if ~hasEntryClass
148 TN(eidx_check) = TN(eidx_check) + actTput;
149 QN(eidx_check) = QN(eidx_check) + self.results{end,e}.QN(serverIdx,c);
150 SN(eidx_check) = SN(eidx_check) + self.results{end,e}.RN(serverIdx,c);
151 end
152 break;
153 end
154 end
155 switch self.ensemble{e}.classes{c}.type
156 case JobClassType.CLOSED
157 TN(aidx) = TN(aidx) + self.results{end,e}.TN(serverIdx,c);
158 case JobClassType.OPEN
159 TN(aidx) = TN(aidx) + self.results{end,e}.TN(sourceIdx,c);
160 end
161 % SN(aidx) = self.servt(aidx);
162 if isnan(SN(aidx)), SN(aidx)=0; end
163 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c);
164 if isnan(RN(aidx)), RN(aidx)=0; end
165 RN(aidx) = RN(aidx) + self.results{end,e}.RN(serverIdx,c);
166 if isnan(WN(aidx)), WN(aidx)=0; end
167 if isnan(WN(tidx)), WN(tidx)=0; end
168 % Use self.residt (computed via QN/TN_ref in updateMetricsDefault)
169 % instead of layer WN to avoid fork+loop visit distortion
170 WN(aidx) = self.residt(aidx);
171 if ~WN_processed(aidx)
172 WN(tidx) = WN(tidx) + self.residt(aidx);
173 WN_processed(aidx) = true;
174 end
175 if isnan(QN(aidx)), QN(aidx)=0; end
176 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
177 end
178 end
179end
180
181for e=1:self.lqn.nentries
182 eidx = self.lqn.eshift + e;
183 tidx = self.lqn.parent(eidx);
184 if isnan(UN(tidx)), UN(tidx)=0; end
185
186 % Phase-2 support: utilization includes both phases
187 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
188 % Phase-1 utilization
189 self.util_ph1(eidx) = TN(eidx) * self.servt_ph1(eidx);
190 % Phase-2 utilization
191 self.util_ph2(eidx) = TN(eidx) * self.servt_ph2(eidx);
192 % Total utilization = both phases (server is busy during both)
193 UN(eidx) = self.util_ph1(eidx) + self.util_ph2(eidx);
194 else
195 % Standard calculation for entries without phase-2
196 UN(eidx) = TN(eidx)*SN(eidx);
197 end
198
199 for aidx=self.lqn.actsof{tidx}
200 UN(aidx) = TN(aidx)*SN(aidx);
201 end
202 UN(tidx) = UN(tidx) + UN(eidx);
203end
204
205for idx=find(self.ignore)
206 QN(idx)=0;
207 UN(idx)=0;
208 RN(idx)=0;
209 TN(idx)=0;
210 PN(idx)=0;
211 SN(idx)=0;
212 WN(idx)=0;
213 AN(idx)=0;
214end
215
216QN = UN;
217UN = PN;
218RN = SN;
219end