Tutorial 8: Response Time Distribution and Percentiles
This example illustrates the computation of response time percentiles in a queueing network model. We compare results from the Fluid solver (analytical steady-state) and LDES simulation (transient).
% Response time distribution and percentiles
model = Network('Model');
% Block 1: nodes
node{1} = Delay(model, 'Delay');
node{2} = Queue(model, 'Queue1', SchedStrategy.PS);
% Block 2: classes
jobclass{1} = ClosedClass(model, 'Class1', 5, node{1}, 0);
node{1}.setService(jobclass{1}, Exp(1.0));
node{2}.setService(jobclass{1}, Exp(0.5));
% Block 3: topology
model.link(Network.serialRouting(node{1},node{2}));
% Block 4: solution
RDfluid = FLD(model,'seed',23000).getCdfRespT();
RDsim = LDES(model,'seed',23000,'samples',1e4).getCdfRespT();
%% Plot results
if ~isempty(RDsim{2,1})
semilogx(RDsim{2,1}(:,2),1-RDsim{2,1}(:,1),'r'); hold on;
semilogx(RDfluid{2,1}(:,2),1-RDfluid{2,1}(:,1),'k--');
legend('ldes-transient','fluid-steady','Location','Best');
ylabel('Pr(T > t)'); xlabel('time t');
end
from line_solver import *
import matplotlib.pyplot as plt
model = Network('Model')
# Block 1: nodes
delay = Delay(model, 'Delay')
queue = Queue(model, 'Queue1', SchedStrategy.PS)
# Block 2: classes
jobclass = ClosedClass(model, 'Class1', 5, delay, 0)
delay.set_service(jobclass, Exp(1.0))
queue.set_service(jobclass, Exp(0.5))
# Block 3: topology
model.link(Network.serial_routing(delay, queue))
# Block 4: solution
RDfluid = FLD(model).cdf_resp_t()
RDsim = LDES(model, seed=23000, samples=10000).cdf_resp_t()
# Plot results
if RDsim[1][0] is not None:
plt.semilogx(RDsim[1][0][:, 1], 1 - RDsim[1][0][:, 0], 'r-', label='ldes-transient')
plt.semilogx(RDfluid[1][0][:, 1], 1 - RDfluid[1][0][:, 0], 'k--', label='fluid-steady')
plt.legend()
plt.ylabel('Pr(T > t)')
plt.xlabel('time t')
plt.show()
import jline.io.Ret.DistributionResult;
import jline.lang.ClosedClass;
import jline.lang.nodes.Delay;
import jline.solvers.fluid.FLD;
import jline.solvers.ldes.LDES;
import jline.util.matrix.Matrix;
Network model = new Network("Model");
Delay delay = new Delay(model, "Delay");
Queue queue = new Queue(model, "Queue1", SchedStrategy.PS);
ClosedClass jobclass = new ClosedClass(model, "Class1", 5, delay, 0);
delay.setService(jobclass, new Exp(1.0));
queue.setService(jobclass, new Exp(0.5));
model.link(Network.serialRouting(delay, queue));
DistributionResult rdFluid = new FLD(model).cdfRespT();
DistributionResult rdSim =
new LDES(model, "seed", 23000, "samples", 10000).cdfRespT();
Matrix cdf = rdSim.getCdf(1, 0); // Queue1, Class1
double prc95 = 0, prc99 = 0;
for (int i = 0; i < cdf.getNumRows(); i++) {
if (cdf.get(i, 0) < 0.95) prc95 = cdf.get(i, 1);
if (cdf.get(i, 0) < 0.99) prc99 = cdf.get(i, 1);
}
System.out.println("95th percentile: " + prc95);
System.out.println("99th percentile: " + prc99);
#include "line/solvers/fluid/fluid_runner.h"
#include "line/solvers/wrappers/ldes/solver_ldes.h"
Network model("Model");
Delay delay(model, "Delay");
Queue queue(model, "Queue1", SchedStrategy::PS);
ClosedClass jobclass(model, "Class1", 5, delay, 0);
delay.set_service(jobclass, Exp(1.0));
queue.set_service(jobclass, Exp(0.5));
Routing P;
P.set(delay, queue, 1.0);
P.set(queue, delay, 1.0);
model.link(P);
const auto rdFluid = fluid::solver_fluid_cdf_respt(
model.get_struct(), fluid::FluidOptions());
// The simulated law: the ecdf of the per-job response times LDES exports.
ldes::LdesOptions sim;
sim.seed = 23000;
sim.samples = 10000;
sim.export_respt = true;
const ldes::LdesResult sres = ldes::solver_ldes(model.get_struct(), sim);
const Matrix<double> rdSim = ldes::ldes_cdf_respt(sres, 1, 0);
const auto& curve = rdFluid[1][0];
double prc95 = 0.0, prc99 = 0.0;
for (std::size_t i = 0; i < curve.cdf.size(); ++i) {
if (curve.cdf[i] < 0.95) prc95 = curve.t[i];
if (curve.cdf[i] < 0.99) prc99 = curve.t[i];
}
Expected Output
The plot shows the complementary CDF of response times comparing LDES simulation (transient) with Fluid solver (steady-state):