1function lqn = lqn_fwd_rendezvous(lqn)
2% LQN = LQN_FWD_RENDEZVOUS(LQN)
3% Port of LQNS Phase::addForwardingRendezvous (phase.cc): replace each
4% forwarding chain reachable from a synchronous call by caller-side pseudo
5% rendezvous (SYNC) calls to
the forwarding targets, with mean equal to
the
6% original call mean times
the product of
the forwarding probabilities on
7%
the path. After this transformation
the forwarded workload
is carried by
8% ordinary SYNC call classes, so layer construction, think times,
9% populations and
the interlock analysis all see plain rendezvous arcs.
10% This matches LQNS, which drops FWD arcs from
the interlock analysis
11% (
"Drop forward -- keep rnv", interlock.cc) and accounts for forwarding
12% only through these pseudo arcs. FWD calls are kept in
the struct but no
13% longer contribute blocking anywhere in SolverLN.
15% Asynchronous calls into a forwarding chain are left untouched (LQNS
16% breaks
the backward search at a send-no-reply, phase.cc).
18if ~any(lqn.calltype == CallType.FWD)
24 if lqn.calltype(cidx) ~= CallType.SYNC
27 aidx = lqn.callpair(cidx,1);
28 tidx = lqn.parent(aidx);
29 base_mean = lqn.callproc_mean(cidx);
33 % BFS through
the forwarding chain of
the sync target
34 frontier = lqn.callpair(cidx,2);
37 while ~isempty(frontier)
38 eidx = frontier(1); frontier(1) = [];
39 p_path = probs(1); probs(1) = [];
40 if ismember(eidx, visitedE),
continue; end
41 visitedE(end+1) = eidx; %#ok<AGROW>
43 if lqn.calltype(fcidx) ~= CallType.FWD || lqn.callpair(fcidx,1) ~= eidx
46 fprob = lqn.callproc_mean(fcidx);
47 tgt = lqn.callpair(fcidx,2);
48 pseudo_mean = base_mean * p_path * fprob;
49 if pseudo_mean > 0 && lqn.parent(tgt) ~= tidx
50 % Merge into an existing SYNC call with
the same
51 % (activity, target) pair,
if any (recurActGraph matches
52 % call classes by callpair row, so duplicates are not
53 % allowed); otherwise append a
new pseudo SYNC call.
55 for scan = 1:lqn.ncalls
56 if lqn.calltype(scan) == CallType.SYNC && ...
57 lqn.callpair(scan,1) == aidx && lqn.callpair(scan,2) == tgt
63 newmean = lqn.callproc_mean(mrow) + pseudo_mean;
64 d = Geometric(1/newmean);
65 lqn.callproc{mrow,1} = d;
66 lqn.callproc_mean(mrow) = newmean;
67 lqn.callproc_scv(mrow) = d.getSCV();
69 ncall = lqn.ncalls + 1;
71 target_tidx = lqn.parent(tgt);
72 d = Geometric(1/pseudo_mean);
73 lqn.calltype(ncall,1) = CallType.SYNC;
74 lqn.callpair(ncall,1:2) = [aidx, tgt];
75 lqn.callnames{ncall,1} = [lqn.names{aidx},
'=>',lqn.names{tgt}];
76 lqn.callhashnames{ncall,1} = [lqn.hashnames{aidx},
'=>',lqn.hashnames{tgt}];
77 lqn.callproc{ncall,1} = d;
78 % Only
the mean (and
the process
object) are consumed by
79 % SolverLN; mirror
the base call
for the remaining fields
80 lqn.callproc_type(ncall) = lqn.callproc_type(cidx);
81 lqn.callproc_params{ncall} = lqn.callproc_params{cidx};
82 lqn.callproc_mean(ncall) = pseudo_mean;
83 lqn.callproc_scv(ncall) = d.getSCV();
84 lqn.callproc_proc{ncall} = lqn.callproc_proc{cidx};
85 lqn.callsof{aidx}(end+1) = ncall;
86 lqn.iscaller(tidx, target_tidx) =
true;
87 lqn.iscaller(aidx, target_tidx) =
true;
88 lqn.iscaller(tidx, tgt) =
true;
89 lqn.iscaller(aidx, tgt) =
true;
90 lqn.issynccaller(tidx, target_tidx) =
true;
91 lqn.issynccaller(aidx, target_tidx) =
true;
92 lqn.issynccaller(tidx, tgt) =
true;
93 lqn.issynccaller(aidx, tgt) =
true;
94 lqn.graph(aidx, tgt) = 1;
95 lqn.taskgraph(tidx, target_tidx) = 1;
99 if ~ismember(tgt, visitedE) && ~ismember(tgt, frontier)
100 frontier(end+1) = tgt; %#ok<AGROW>
101 probs(end+1) = p_path * fprob; %#ok<AGROW>