LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
lqn_fwd_rendezvous.m
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.
14%
15% Asynchronous calls into a forwarding chain are left untouched (LQNS
16% breaks the backward search at a send-no-reply, phase.cc).
17
18if ~any(lqn.calltype == CallType.FWD)
19 return
20end
21
22ncalls0 = lqn.ncalls;
23for cidx = 1:ncalls0
24 if lqn.calltype(cidx) ~= CallType.SYNC
25 continue
26 end
27 aidx = lqn.callpair(cidx,1);
28 tidx = lqn.parent(aidx);
29 base_mean = lqn.callproc_mean(cidx);
30 if base_mean <= 0
31 continue
32 end
33 % BFS through the forwarding chain of the sync target
34 frontier = lqn.callpair(cidx,2);
35 probs = 1;
36 visitedE = [];
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>
42 for fcidx = 1:ncalls0
43 if lqn.calltype(fcidx) ~= CallType.FWD || lqn.callpair(fcidx,1) ~= eidx
44 continue
45 end
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 % see _kb/06-solver-catalog.md (LN section) for rationale
51 mrow = 0;
52 for scan = 1:lqn.ncalls
53 if lqn.calltype(scan) == CallType.SYNC && ...
54 lqn.callpair(scan,1) == aidx && lqn.callpair(scan,2) == tgt
55 mrow = scan;
56 break
57 end
58 end
59 if mrow > 0
60 newmean = lqn.callproc_mean(mrow) + pseudo_mean;
61 d = Geometric(1/newmean);
62 lqn.callproc{mrow,1} = d;
63 lqn.callproc_mean(mrow) = newmean;
64 lqn.callproc_scv(mrow) = d.getSCV();
65 else
66 ncall = lqn.ncalls + 1;
67 lqn.ncalls = ncall;
68 target_tidx = lqn.parent(tgt);
69 d = Geometric(1/pseudo_mean);
70 lqn.calltype(ncall,1) = CallType.SYNC;
71 lqn.callpair(ncall,1:2) = [aidx, tgt];
72 lqn.callnames{ncall,1} = [lqn.names{aidx},'=>',lqn.names{tgt}];
73 lqn.callhashnames{ncall,1} = [lqn.hashnames{aidx},'=>',lqn.hashnames{tgt}];
74 lqn.callproc{ncall,1} = d;
75 % Only the mean (and the process object) are consumed by
76 % SolverLN; mirror the base call for the remaining fields
77 lqn.callproc_type(ncall) = lqn.callproc_type(cidx);
78 lqn.callproc_params{ncall} = lqn.callproc_params{cidx};
79 lqn.callproc_mean(ncall) = pseudo_mean;
80 lqn.callproc_scv(ncall) = d.getSCV();
81 lqn.callproc_proc{ncall} = lqn.callproc_proc{cidx};
82 lqn.callsof{aidx}(end+1) = ncall;
83 lqn.iscaller(tidx, target_tidx) = true;
84 lqn.iscaller(aidx, target_tidx) = true;
85 lqn.iscaller(tidx, tgt) = true;
86 lqn.iscaller(aidx, tgt) = true;
87 lqn.issynccaller(tidx, target_tidx) = true;
88 lqn.issynccaller(aidx, target_tidx) = true;
89 lqn.issynccaller(tidx, tgt) = true;
90 lqn.issynccaller(aidx, tgt) = true;
91 lqn.graph(aidx, tgt) = 1;
92 lqn.taskgraph(tidx, target_tidx) = 1;
93 end
94 end
95 % Follow the chain
96 if ~ismember(tgt, visitedE) && ~ismember(tgt, frontier)
97 frontier(end+1) = tgt; %#ok<AGROW>
98 probs(end+1) = p_path * fprob; %#ok<AGROW>
99 end
100 end
101 end
102end
103end