1% LQN_TRANSIENT Transient (time-dependent) analysis of a layered network.
3% Demonstrates LN.getTranAvg, which returns
the transient mean queue
4% length, utilization and throughput of every ensemble layer over time. The
5% traces are assembled block-diagonally: layer e occupies a disjoint block of
6% rows (its stations) and columns (its classes). Transient output
is only
7% produced by transient-capable per-layer
solvers (Fluid, CTMC, SSA); here
the
8% layers are solved with
the fluid ODE solver. Do not set a
'timespan' on
the
9% per-layer factory:
the steady-state fixed point rejects it, and getTranAvg
10%
auto-selects
the timespan per layer.
12% getTranAvg first converges
the LN fixed point (getAvg), pinning
the
13% inter-layer demands to equilibrium, then runs each layer
's transient with
14% those demands frozen. The initial point is the layer's
default state (all
15% closed jobs at
the reference station, via State.initDefault), NOT
the
16% converged occupancy, so each curve relaxes from all-at-reference to
the
17% layer steady state. Use setState to seed a different start.
19% The model
is a simple 3-layer LQN: a reference task T1 on processor P1 whose
20% activity synchronously calls entry E2 of task T2 on processor P2. The three
21% ensemble layers are
the two processor (host) layers and
the T2 task layer.
23model = LayeredNetwork(
'lqn_transient');
25P1 = Processor(model,
'P1', 1, SchedStrategy.PS);
26P2 = Processor(model,
'P2', 1, SchedStrategy.PS);
28T1 = Task(model,
'T1', 5, SchedStrategy.REF).on(P1).setThinkTime(Exp(1.0));
29T2 = Task(model,
'T2', 5, SchedStrategy.FCFS).on(P2).setThinkTime(Exp(1.0));
31E1 = Entry(model,
'E1').on(T1);
32E2 = Entry(model,
'E2').on(T2);
34A1 = Activity(model,
'A1', Exp(2.0)).on(T1).boundTo(E1).synchCall(E2, 1);
35A2 = Activity(model,
'A2', Exp(3.0)).on(T2).boundTo(E2).repliesTo(E2);
37% Solve
the ensemble with a fluid solver on each layer, then obtain
the
38% per-layer transient averages in a single call.
39solver = LN(model, @(m) FLD(m,
'verbose',
false),
'verbose',
false);
40[QNt, UNt, TNt] = solver.getTranAvg();
43fprintf(
'LN.getTranAvg returned transient traces for %d layers.\n', E);
45% Plot
the transient mean queue length E[N](t) of each station, one panel per
46% layer. The block-diagonal offsets (r0,c0) advance by each layer
's station and
47% class counts, mirroring how getTranAvg stacks the per-layer blocks.
48figure('Name
', 'LN.getTranAvg: per-layer transient E[N](t)
');
52 sn = solver.ensemble{e}.getStruct();
62 trace = QNt{r0 + i, c0 + r};
63 if isstruct(trace) && isfield(trace, 'metric
') && any(trace.metric > 1e-6)
64 stname = trace.handle{1}.name;
65 clname = trace.handle{2}.name;
66 plot(trace.t, trace.metric, 'LineWidth
', 1.5, ...
67 'DisplayName
', sprintf('%s [%s]
', stname, clname));
68 % Track when this trace stops changing so the axis can be
69 % zoomed onto the transient (getTranAvg auto-selects a wide
70 % timespan and the curves are flat once settled).
73 tol = 0.01 * (max(m) - min(m)) + 1e-9;
74 klast = find(abs(m - m(end)) > tol, 1, 'last
');
76 tsettle = max(tsettle, trace.t(min(klast + 1, numel(m))));
85 xlim([0, 1.3 * tsettle]);
87 title(sprintf('Layer %d
', e));
90 legend('Location
', 'best
');