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
28E = self.nlayers;
29for e=1:E
30 clientIdx = self.ensemble{e}.attribute.clientIdx;
31 serverIdx = self.ensemble{e}.attribute.serverIdx;
32 sourceIdx = self.ensemble{e}.attribute.sourceIdx;
33 % determine processor metrics
34 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
35 hidx = self.ensemble{e}.stations{serverIdx}.attribute.idx;
36 TN(hidx) = 0;
37 PN(hidx) = 0;
38 for c=1:self.ensemble{e}.getNumberOfClasses
39 if self.ensemble{e}.classes{c}.completes
40 t = 0;
41 u = 0;
42 if ~isnan(clientIdx)
43 t = max(t, self.results{end,e}.TN(clientIdx,c));
44 end
45 if ~isnan(sourceIdx)
46 t = max(t, self.results{end,e}.TN(sourceIdx,c));
47 end
48 TN(hidx) = TN(hidx) + max(t,self.results{end,e}.TN(serverIdx,c));
49 end
50 type = self.ensemble{e}.classes{c}.attribute(1);
51 switch type
52 case LayeredNetworkElement.ACTIVITY
53 aidx = self.ensemble{e}.classes{c}.attribute(2);
54 tidx = self.lqn.parent(aidx);
55 if isnan(PN(aidx)), PN(aidx)=0; end
56 if isnan(PN(tidx)), PN(tidx)=0; end
57 PN(aidx) = PN(aidx) + self.results{end,e}.UN(serverIdx,c);
58 PN(tidx) = PN(tidx) + self.results{end,e}.UN(serverIdx,c);
59 PN(hidx) = PN(hidx) + self.results{end,e}.UN(serverIdx,c);
60 end
61 end
62 TN(hidx) = NaN; % added for consistency with LQNS
63 end
64
65 % determine remaining metrics
66 for c=1:self.ensemble{e}.getNumberOfClasses
67 type = self.ensemble{e}.classes{c}.attribute(1);
68 switch type
69 case LayeredNetworkElement.TASK
70 tidx = self.ensemble{e}.classes{c}.attribute(2);
71 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
72 if isnan(TN(tidx))
73 % store the result in the processor
74 % model
75 TN(tidx) = self.results{end,e}.TN(clientIdx,c);
76 end
77 else
78 % nop
79 end
80 case LayeredNetworkElement.ENTRY
81 eidx = self.ensemble{e}.classes{c}.attribute(2);
82 tidx = self.lqn.parent(eidx);
83 % For phase-2 models, use residt (caller's view with overtaking)
84 % Otherwise use servt (total service time = response time)
85 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
86 SN(eidx) = self.residt(eidx); % Phase-1 + overtaking correction
87 else
88 SN(eidx) = self.servt(eidx);
89 end
90 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
91 if isnan(TN(eidx))
92 % store the result in the processor model
93 if isnan(TN(eidx)), TN(eidx)=0; end
94 TN(eidx) = self.results{end,e}.TN(clientIdx,c);
95 end
96 else
97 % nop
98 end
99 case LayeredNetworkElement.CALL
100 cidx = self.ensemble{e}.classes{c}.attribute(2);
101 aidx = self.lqn.callpair(cidx,1);
102 % Only sync calls contribute to caller's response time
103 if self.lqn.calltype(cidx) == CallType.SYNC
104 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c) * self.lqn.callproc{cidx}.getMean();
105 end
106 if isnan(QN(aidx)), QN(aidx)=0; end
107 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
108 case LayeredNetworkElement.ACTIVITY
109 aidx = self.ensemble{e}.classes{c}.attribute(2);
110 tidx = self.lqn.parent(aidx);
111 if isnan(QN(tidx)), QN(tidx)=0; end
112 QN(tidx) = QN(tidx) + self.results{end,e}.QN(serverIdx,c);
113 if isnan(TN(aidx)), TN(aidx)=0; end
114 if isnan(QN(aidx)), QN(aidx)=0; end
115
116 % For forwarding targets: propagate activity metrics to entry and task
117 % Check if task has its own class (non-forwarding targets do)
118 hasTaskClass = any(self.ensemble{e}.attribute.tasks(:,2) == tidx);
119 if ~hasTaskClass
120 % Propagate activity throughput to task for forwarding targets
121 if isnan(TN(tidx)), TN(tidx)=0; end
122 switch self.ensemble{e}.classes{c}.type
123 case JobClassType.CLOSED
124 TN(tidx) = TN(tidx) + self.results{end,e}.TN(serverIdx,c);
125 case JobClassType.OPEN
126 TN(tidx) = TN(tidx) + self.results{end,e}.TN(sourceIdx,c);
127 end
128 end
129
130 % Find the entry this activity is bound to
131 for eidx_check = self.lqn.entriesof{tidx}
132 if self.lqn.graph(eidx_check, aidx) > 0 % Activity is bound to this entry
133 if isnan(TN(eidx_check)), TN(eidx_check)=0; end
134 if isnan(QN(eidx_check)), QN(eidx_check)=0; end
135 if isnan(SN(eidx_check)), SN(eidx_check)=0; end
136 % Add activity metrics to entry (for entries without their own classes)
137 actTput = 0;
138 switch self.ensemble{e}.classes{c}.type
139 case JobClassType.CLOSED
140 actTput = self.results{end,e}.TN(serverIdx,c);
141 case JobClassType.OPEN
142 actTput = self.results{end,e}.TN(sourceIdx,c);
143 end
144 % Only add if entry doesn't have its own class (forwarding target case)
145 hasEntryClass = any(self.ensemble{e}.attribute.entries(:,2) == eidx_check);
146 if ~hasEntryClass
147 TN(eidx_check) = TN(eidx_check) + actTput;
148 QN(eidx_check) = QN(eidx_check) + self.results{end,e}.QN(serverIdx,c);
149 SN(eidx_check) = SN(eidx_check) + self.results{end,e}.RN(serverIdx,c);
150 end
151 break;
152 end
153 end
154 switch self.ensemble{e}.classes{c}.type
155 case JobClassType.CLOSED
156 TN(aidx) = TN(aidx) + self.results{end,e}.TN(serverIdx,c);
157 case JobClassType.OPEN
158 TN(aidx) = TN(aidx) + self.results{end,e}.TN(sourceIdx,c);
159 end
160 % SN(aidx) = self.servt(aidx);
161 if isnan(SN(aidx)), SN(aidx)=0; end
162 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c);
163 if isnan(RN(aidx)), RN(aidx)=0; end
164 RN(aidx) = RN(aidx) + self.results{end,e}.RN(serverIdx,c);
165 if isnan(WN(aidx)), WN(aidx)=0; end
166 if isnan(WN(tidx)), WN(tidx)=0; end
167 WN(aidx) = WN(aidx) + self.results{end,e}.WN(serverIdx,c);
168 WN(tidx) = WN(tidx) + self.results{end,e}.WN(serverIdx,c);
169 if isnan(QN(aidx)), QN(aidx)=0; end
170 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
171 end
172 end
173end
174
175for e=1:self.lqn.nentries
176 eidx = self.lqn.eshift + e;
177 tidx = self.lqn.parent(eidx);
178 if isnan(UN(tidx)), UN(tidx)=0; end
179
180 % Phase-2 support: utilization includes both phases
181 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
182 % Phase-1 utilization
183 self.util_ph1(eidx) = TN(eidx) * self.servt_ph1(eidx);
184 % Phase-2 utilization
185 self.util_ph2(eidx) = TN(eidx) * self.servt_ph2(eidx);
186 % Total utilization = both phases (server is busy during both)
187 UN(eidx) = self.util_ph1(eidx) + self.util_ph2(eidx);
188 else
189 % Standard calculation for entries without phase-2
190 UN(eidx) = TN(eidx)*SN(eidx);
191 end
192
193 for aidx=self.lqn.actsof{tidx}
194 UN(aidx) = TN(aidx)*SN(aidx);
195 end
196 UN(tidx) = UN(tidx) + UN(eidx);
197end
198
199for idx=find(self.ignore)
200 QN(idx)=0;
201 UN(idx)=0;
202 RN(idx)=0;
203 TN(idx)=0;
204 PN(idx)=0;
205 SN(idx)=0;
206 WN(idx)=0;
207 AN(idx)=0;
208end
209
210QN = UN;
211UN = PN;
212RN = SN;
213end