LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
updateThinkTimes.m
1function updateThinkTimes(self, it)
2% Update the think times of all callers at iteration it. The method handles
3% differently the case where a caller is a ref task than the case where the
4% caller is a queueing station. A coarse heuristic is used when one or more
5% callers are themselves infinite servers.
6
7% create local variable due to MATLAB's slow access to self properties
8lqn = self.lqn;
9idxhash = self.idxhash;
10results = self.results;
11
12% main code starts here
13if size(lqn.iscaller,2) > 0 % ignore models without callers
14 torder = 1:(lqn.ntasks); % set sequential order to update the tasks
15 % solve all task models
16 for t = torder
17 tidx = lqn.tshift + t;
18 tidx_thinktime = lqn.think{tidx}.getMean; % user specified think time
19 %if ~lqn.isref(tidx) && ~isnan(idxhash(tidx)) % update tasks ignore ref tasks and empty tasks
20 if ~isnan(self.idxhash(tidx)) % this skips all REF tasks
21 % obtain total self.tput of task t
22 % mean throughput of task t in the model where it is a server, summed across replicas
23 njobs = max(self.njobs(tidx,:)); % we use njobs to adapt to interlocking corrections
24 self.tput(tidx) = lqn.repl(tidx)*sum(results{end,idxhash(tidx)}.TN(self.ensemble{idxhash(tidx)}.attribute.serverIdx,:),2);
25 if lqn.sched(tidx) == SchedStrategy.INF % first we consider the update where t is an infinite server
26 % obtain total self.utilization of task t
27 self.util(tidx) = sum(results{end,idxhash(tidx)}.UN(self.ensemble{idxhash(tidx)}.attribute.serverIdx,:),2);
28 % key think time update formula for LQNs, this accounts for the fact that in LINE infinite server self.utilization is dimensionally a mean number of jobs
29 self.thinkt(tidx) = max(GlobalConstants.Zero, (njobs-self.util(tidx)) / self.tput(tidx) - tidx_thinktime);
30 else % otherwise we consider the case where t is a regular queueing station (other than an infinite server)
31 self.util(tidx) = sum(results{end,idxhash(tidx)}.UN(self.ensemble{idxhash(tidx)}.attribute.serverIdx,:),2); % self.utilization of t as a server
32 % key think time update formula for LQNs, this accounts that in LINE self.utilization is scaled in [0,1] for all queueing stations irrespectively of the number of servers
33 self.thinkt(tidx) = max(GlobalConstants.Zero, njobs*abs(1-self.util(tidx)) / self.tput(tidx) - tidx_thinktime);
34 end
35 % Recover from Inf/NaN: snap back to previous iteration's value
36 if it > 1 && ~isnan(self.thinkt_prev(tidx))
37 if isinf(self.thinkt(tidx)) || isnan(self.thinkt(tidx))
38 self.thinkt(tidx) = self.thinkt_prev(tidx);
39 end
40 end
41 % Apply under-relaxation to think time if enabled
42 omega = self.relax_omega;
43 if omega < 1.0 && it > 1 && ~isnan(self.thinkt_prev(tidx))
44 rawT = self.thinkt(tidx);
45 prevT = self.thinkt_prev(tidx);
46 % If recovering from crash (prev much larger than raw), snap to raw
47 if prevT > 10 * rawT && rawT > GlobalConstants.FineTol
48 self.thinkt_prev(tidx) = rawT; % reset prev to allow recovery
49 end
50 self.thinkt(tidx) = omega * self.thinkt(tidx) + (1 - omega) * self.thinkt_prev(tidx);
51 end
52 self.thinkt_prev(tidx) = self.thinkt(tidx);
53 self.thinktproc{tidx} = Exp.fitMean(self.thinkt(tidx) + tidx_thinktime);
54 else % ref task or forwarding target (no task layer)
55 % Check if this is a forwarding target task
56 isFwdTarget = false;
57 if ~lqn.isref(tidx)
58 for eidx_fwd = lqn.entriesof{tidx}
59 for cidx_fwd = 1:lqn.ncalls
60 if lqn.calltype(cidx_fwd) == CallType.FWD && lqn.callpair(cidx_fwd, 2) == eidx_fwd
61 isFwdTarget = true;
62 source_eidx = lqn.callpair(cidx_fwd, 1);
63 source_tidx = lqn.parent(source_eidx);
64 fwd_prob = lqn.callproc{cidx_fwd}.getMean();
65 break;
66 end
67 end
68 if isFwdTarget; break; end
69 end
70 end
71 if isFwdTarget
72 % see _kb/06-solver-catalog.md (LN section) for rationale
73 njobs = max(self.njobs(tidx,:));
74 tidx_thinktime = lqn.think{tidx}.getMean;
75 arrival_rate = self.tput(source_tidx) * fwd_prob;
76 if arrival_rate > GlobalConstants.FineTol && njobs > 0
77 self.tput(tidx) = arrival_rate;
78 % Subtract the processor response time for the target's
79 % activities (already computed by updateMetricsDefault)
80 target_eidx = lqn.callpair(cidx_fwd, 2);
81 host_residt = 0;
82 for aidx_fwd = lqn.actsof{target_eidx}
83 host_residt = host_residt + self.residt(aidx_fwd);
84 end
85 self.thinkt(tidx) = max(GlobalConstants.Zero, njobs / arrival_rate - host_residt - tidx_thinktime);
86 else
87 % Source throughput not yet available; use large think time
88 self.thinkt(tidx) = 1000;
89 end
90 % Apply under-relaxation
91 omega = self.relax_omega;
92 if omega < 1.0 && it > 1 && ~isnan(self.thinkt_prev(tidx))
93 self.thinkt(tidx) = omega * self.thinkt(tidx) + (1 - omega) * self.thinkt_prev(tidx);
94 end
95 self.thinkt_prev(tidx) = self.thinkt(tidx);
96 self.thinktproc{tidx} = Exp.fitMean(self.thinkt(tidx) + tidx_thinktime);
97 else
98 self.thinkt(tidx) = GlobalConstants.FineTol;
99 self.thinktproc{tidx} = Immediate();
100 end
101 end
102 end
103end
104end