1function updatePopulations(self, it)
2% UPDATEPOPULATIONS Apply interlock correction to call residence times
4% Uses LQNS V5-style interlock analysis: computes interlock probability
5%
for each (client, server) pair and reduces the waiting time component
6% of call residence times proportionally.
8% LQNS applies interlock as arrival-rate scaling inside MVA:
9% L_k = (1 - prIL_k) * X_k * R_k
10% affecting only waiting time, not utilization. LINE approximates
this
11% by adjusting callresidt post-MVA to remove interlocked waiting:
12% R_adj = S + (1 - prIL) * W
13% where S = service time, W = waiting time = R - S.
15% This function
is called AFTER updateMetrics (which computes raw
16% callresidt from layer MVA results) and BEFORE updateThinkTimes.
20% Save originals
for proportional entry_servt update
21callresidt_orig = self.callresidt(:);
22residt_orig = self.residt(:);
25% For each sync call, check
if destination server has interlock
26for cidx = 1:lqn.ncalls
27 if lqn.calltype(cidx) ~= CallType.SYNC
31 dst_eidx = lqn.callpair(cidx, 2);
32 server_tidx = lqn.parent(dst_eidx);
34 % Find the server entity with interlock data
36 if server_tidx <= length(self.il_common_entries) && ~isempty(self.il_common_entries{server_tidx})
37 server_for_il = server_tidx;
40 if server_tidx > lqn.tshift
41 host_idx = lqn.parent(server_tidx);
42 if host_idx >= 1 && host_idx <= length(self.il_common_entries) && ~isempty(self.il_common_entries{host_idx})
43 server_for_il = host_idx;
47 if isempty(server_for_il)
51 % Get client task (activity -> task via parent)
52 src_aidx = lqn.callpair(cidx, 1);
53 client_tidx = lqn.parent(src_aidx);
55 % Compute prIL
using interlockedFlow formula
56 prIL = computeInterlockProb(self, lqn, client_tidx, server_for_il);
58 if prIL <= GlobalConstants.FineTol
62 % Compute waiting time reduction
63 S = self.servt(dst_eidx); % service time at destination entry
64 call_mean = lqn.callproc_mean(cidx);
66 if call_mean <= 0 || self.callservt(cidx) <= 0
70 RN = self.callservt(cidx) / call_mean; % response time per visit
71 W = max(0, RN - S); % waiting time per visit
73 if W > GlobalConstants.FineTol
74 RN_adj = S + (1 - prIL) * W;
76 self.callservt(cidx) = self.callservt(cidx) * scale;
77 self.callresidt(cidx) = self.callresidt(cidx) * scale;
78 if self.callservt(cidx) > 0
79 self.callservtproc{cidx} = Exp.fitMean(self.callservt(cidx));
85% Pass 2: Host-level interlock — reduce processor queueing in residt
88 if isempty(self.il_common_entries{hidx})
92 % Compute prIL and processor utilization for each task on this host
93 host_tasks = lqn.tasksof{hidx}(:)
';
94 task_prIL = zeros(length(host_tasks), 1);
95 task_util = zeros(length(host_tasks), 1);
96 for ti = 1:length(host_tasks)
97 tidx = host_tasks(ti);
98 task_prIL(ti) = computeInterlockProb(self, lqn, tidx, hidx);
99 % Compute task's processor utilization
100 for eidx = lqn.entriesof{tidx}(:)
'
101 for aidx = lqn.actsof{eidx}(:)'
102 task_util(ti) = task_util(ti) + self.tput(aidx) * lqn.hostdem_mean(aidx);
107 U_total = sum(task_util);
108 U_interlocked = sum(task_util(task_prIL > GlobalConstants.FineTol));
109 if U_total <= GlobalConstants.FineTol || U_interlocked <= GlobalConstants.FineTol
112 il_fraction = U_interlocked / U_total;
114 for ti = 1:length(host_tasks)
115 if task_prIL(ti) <= GlobalConstants.FineTol
118 tidx = host_tasks(ti);
119 % Scale prIL by fraction of utilization that
is interlocked
120 effective_prIL = task_prIL(ti) * il_fraction;
121 for eidx = lqn.entriesof{tidx}(:)
'
122 for aidx = lqn.actsof{eidx}(:)'
123 D = lqn.hostdem_mean(aidx);
124 if D > 0 && self.residt(aidx) > D + GlobalConstants.FineTol
125 W_proc = self.residt(aidx) - D;
126 self.residt(aidx) = D + (1 - effective_prIL) * W_proc;
138% Recompute entry service times from adjusted callresidt/residt
139% Use proportional scaling to preserve visit ratio adjustments
140% applied in updateMetricsDefault
141entry_servt_old = self.servtmatrix * [residt_orig; callresidt_orig];
142entry_servt_new = self.servtmatrix * [self.residt; self.callresidt(:)];
144for eidx = (lqn.eshift+1):(lqn.eshift+lqn.nentries)
145 if entry_servt_old(eidx) > GlobalConstants.FineTol
146 ratio = entry_servt_new(eidx) / entry_servt_old(eidx);
147 self.servt(eidx) = self.servt(eidx) * ratio;
148 self.residt(eidx) = self.residt(eidx) * ratio;
149 if self.servt(eidx) > 0
150 self.servtproc{eidx} = Exp.fitMean(self.servt(eidx));
156%% Compute interlock probability
for a (client, server) pair
157function prIL = computeInterlockProb(self, lqn, client_tidx, server_idx)
158commonEntries = self.il_common_entries{server_idx};
159numSources = self.il_num_sources(server_idx);
160allSrcTasks = self.il_source_tasks_all{server_idx};
161ph2SrcTasks = self.il_source_tasks_ph2{server_idx};
163if numSources == 0 || isempty(commonEntries)
169client_entries = lqn.entriesof{client_tidx};
171% Compute interlocked flow (LQNS interlockedFlow formula)
173for ce_eidx = commonEntries(:)
'
174 srcTask = lqn.parent(ce_eidx);
175 ce_num = ce_eidx - lqn.eshift;
177 for dstA_eidx = client_entries(:)'
178 dstA_num = dstA_eidx - lqn.eshift;
180 if dstA_num < 1 || dstA_num > lqn.nentries
183 if self.il_table_all(ce_num, dstA_num) <= 0
187 % Get source entry throughput
188 ce_tput = getEntryTput(self, lqn, ce_eidx, srcTask);
190 if ce_tput <= GlobalConstants.FineTol
194 % Check maxPhase
for the source entry
195 hasP2 = hasPhase2Check(lqn, ce_eidx);
197 if ~hasP2 && ismember(srcTask, allSrcTasks)
198 sum_flow = sum_flow + ce_tput * self.il_table_all(ce_num, dstA_num);
199 elseif hasP2 && ismember(srcTask, allSrcTasks)
200 sum_flow = sum_flow + ce_tput * self.il_table_ph1(ce_num, dstA_num);
203 ph2 = self.il_table_all(ce_num, dstA_num) - self.il_table_ph1(ce_num, dstA_num);
204 if ph2 > 0 && ismember(srcTask, ph2SrcTasks)
205 sum_flow = sum_flow + ce_tput * ph2;
210% Get client throughput
211client_tput = getTaskTput(self, lqn, client_tidx);
213if client_tput <= GlobalConstants.FineTol
218client_threads = lqn.mult(client_tidx);
219IL = min(sum_flow, client_tput) / (client_tput * client_threads * numSources);
220prIL = IL / lqn.mult(server_idx);
221prIL = min(prIL, 1.0);
224%% Helper: get entry throughput
225function tput = getEntryTput(self, lqn, eidx, taskIdx)
226tput = self.tput(eidx);
227if tput <= GlobalConstants.FineTol
229 acts = lqn.actsof{eidx};
231 tput = self.tput(acts(1));
234if tput <= GlobalConstants.FineTol
235 tput = self.tput(taskIdx);
239%% Helper: get task throughput
240function tput = getTaskTput(self, lqn, tidx)
241tput = self.tput(tidx);
242if tput <= GlobalConstants.FineTol
243 for eidx = lqn.entriesof{tidx}(:)
'
244 et = self.tput(eidx);
245 if et <= GlobalConstants.FineTol
246 acts = lqn.actsof{eidx};
248 et = self.tput(acts(1));
256%% Helper: check if entry has phase-2 activities
257function result = hasPhase2Check(lqn, eidx)
259if ~isfield(lqn, 'actphase
')
262for aidx = lqn.actsof{eidx}(:)'
263 a = aidx - lqn.ashift;
264 if a > 0 && a <= lqn.nacts && lqn.actphase(a) > 1