1%{ @file fj_tail_forktail.m
2 % @brief ForkTail black-box tail-latency approximation
for fork-join requests
4 % @author LINE Development Team
8 % @brief ForkTail black-box tail-latency approximation
for fork-join requests
11 % Approximates the p-th percentile of the response time of a request that
12 % forks into K parallel tasks and joins on the last of them, from the mean
13 % and variance of the per-branch task response times alone. Each branch
is
14 % treated as a black box: its task response time
is fitted by a generalized
17 % F_T(x) = (1 - exp(-x/beta))^alpha
19 % whose two parameters are matched on the branch mean and variance,
21 % E[T] = beta*(psi(alpha+1) - psi(1)), V[T] = beta^2*(psi
'(1) - psi'(alpha+1)),
23 % and the request response time
is the maximum over the branches, taken as
24 % the product of the branch CDFs (exact only
for independent branches):
26 % F_X(x) = prod_i (1 - exp(-x/beta_i))^alpha_i, x_p = F_X^{-1}(p).
28 % In the homogeneous
case this inverts in closed
form,
29 % x_p = -beta*log(1 - p^(1/(K*alpha))).
31 % When the fanout itself
is random, i.e. a request spawns K_i tasks with
32 % probability P_i (a service whose requests touch different numbers of
33 % shards), the request law
is the mixture
35 % F_X(x) = sum_i P_i * (1 - exp(-x/beta))^(K_i*alpha),
37 % which
is inverted numerically.
39 % The approximation rests on the central limit theorem
for G/G/m queues in
40 % heavy traffic, so it
is a HIGH-LOAD result: the reference reports errors
41 % within 20% and 15% at 80% and 90% utilization respectively, and makes no
42 % claim at low load, where the tail
is dominated by the service law rather
43 % than by queueing and the branch dependence
is strongest. Use
44 % fj_is_homogeneous plus the FJ_codes route when the model
is in the
45 % homogeneous MAP/PH/1
class, which
is more accurate there; ForkTail covers
46 % the heterogeneous branches and mixed service laws that route rejects.
50 % [xp, alpha, beta] = fj_tail_forktail(ET, VT, K, p)
55 % <tr><th>Name<th>Description
56 % <tr><td>ET<td>Mean task response time: a scalar (homogeneous branches) or a vector with one entry per branch
57 % <tr><td>VT<td>Variance of the task response time, same shape as ET
58 % <tr><td>K<td>Number of branches, or a vector of distinct fanouts when the fanout
is random; ignored when ET
is a vector (
default 1)
59 % <tr><td>p<td>Percentile, either a fraction in (0,1) or a percentage in (0,100) (default 99)
60 % <tr><td>
P<td>Probabilities of the fanouts in K, required when K
is a vector of more than one entry
65 % <tr><th>Name<th>Description
66 % <tr><td>xp<td>Predicted p-th percentile of the request response time
67 % <tr><td>alpha<td>Fitted shape parameter(s) of the generalized exponential
68 % <tr><td>beta<td>Fitted scale parameter(s)
72 % M. Nguyen, S. Alesawi, N. Li, H. Che, H. Jiang, "ForkTail: A Black-Box
73 % Fork-Join Tail Latency Prediction Model for User-Facing Datacenter
74 % Workloads", ACM HPDC 2018, pp. 206-217.
76function [xp, alpha, beta] = fj_tail_forktail(ET, VT, K, p,
P)
78if nargin < 3 || isempty(K)
81if nargin < 4 || isempty(p)
93 line_error(mfilename, 'The percentile must lie strictly between 0 and 1 (or 0 and 100).');
98if numel(VT) ~= numel(ET)
99 line_error(mfilename, 'ET and VT must have the same number of entries.');
101if any(ET <= 0) || any(VT <= 0)
102 line_error(mfilename, 'The task response time mean and variance must be positive.');
106alpha = zeros(1, nbranch);
107beta = zeros(1, nbranch);
109 [alpha(i), beta(i)] = ge_fit(ET(i), VT(i));
112if nbranch == 1 && numel(K) > 1
113 % Random fanout: mix the homogeneous request laws over the fanout
114 % distribution and invert numerically
115 if numel(
P) ~= numel(K)
116 line_error(mfilename, 'A vector of fanouts K needs a probability vector
P of the same length.');
118 if abs(sum(
P) - 1) > GlobalConstants.CoarseTol || any(
P < 0)
119 line_error(mfilename, 'The fanout probabilities
P must be non-negative and sum to one.');
121 mixcdf = @(x) sum(
P .* (1 - exp(-x/beta)).^(K*alpha));
122 xlo = -beta * log(1 - p^(1/(min(K)*alpha)));
123 xhi = -beta * log(1 - p^(1/(max(K)*alpha)));
124 xp = fzero(@(x) mixcdf(x) - p, [xlo, xhi]);
129 % Homogeneous: every branch shares (alpha,beta), so the product of K
130 % identical CDFs raises the shape to K*alpha and inverts in closed
form
131 xp = -beta * log(1 - p^(1/(K*alpha)));
135% Inhomogeneous: solve prod_i (1-exp(-x/beta_i))^alpha_i = p. F_X
is bounded
136% above by the CDF of any single branch, so the request percentile
is at
137% least the largest branch percentile; expand from there until the root
is
140residual = @(x) sum(alpha .* log1p(-exp(-x ./ beta))) - logp;
141xlo = max(-beta .* log(1 - p.^(1 ./ alpha)));
143while residual(xhi) < 0
146 line_error(mfilename, 'Could not bracket the ForkTail percentile.');
151 while residual(xlo) > 0 && xlo > eps
155xp = fzero(residual, [xlo, xhi]);
158function [alpha, beta] = ge_fit(ET, VT)
159% [ALPHA, BETA] = GE_FIT(ET, VT)
160% Match a generalized exponential law on a mean and a variance. The squared
161% coefficient of variation depends on the shape alone and decreases
162% monotonically in it, so the shape
is recovered by a scalar root-find on a
163% logarithmic scale and the scale then follows in closed
form. SCV = 1
is the
164% exponential case alpha = 1, kept exact.
166if abs(scv - 1) < GlobalConstants.FineTol
169 scvOf = @(a) (psi(1,1) - psi(1,a+1)) / (psi(0,a+1) - psi(0,1))^2;
170 residual = @(la) scvOf(exp(la)) - scv;
172 while residual(lo) < 0 && lo > -700
173 lo = lo - 30; % smaller shape -> larger SCV
175 while residual(hi) > 0 && hi < 700
176 hi = hi + 30; % larger shape -> smaller SCV
178 alpha = exp(fzero(residual, [lo, hi]));
180beta = ET / (psi(0,alpha+1) - psi(0,1));