LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
qsys_mg1_lrpt.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_API_QSYS_QSYS_MG1_LRPT_H
6
#define LINE_API_QSYS_QSYS_MG1_LRPT_H
7
8
/**
9
* @file
10
* @ingroup api_qsys
11
* M/G/1 under LRPT (longest remaining processing time).
12
*
13
* Templated port of matlab/src/api/qsys/qsys_mg1_lrpt.m, cross-checked against
14
* jar/src/main/java/jline/api/qsys/Qsys_mg1_lrpt.java.
15
*
16
* LRPT gives the server to the job with the most work left, so the slowdown is
17
* the same for every job in the system (Wierman and Harchol-Balter,
18
* SIGMETRICS 2003, Sec. 3.2):
19
*
20
* E[T(x)] = x/(1-rho) + lambda E[X^2] / (2 (1-rho)^2)
21
*
22
* with rho the total load and E[X^2] the second moment of the mixture size.
23
* Only the first term depends on x, so under the exponential path the class
24
* mean is
25
*
26
* E[T_k] = int_0^{20/mu_k} E[T(x)] mu_k e^{-mu_k x} dx
27
*
28
* and this integral is elementary. MATLAB evaluates it by adaptive quadrature;
29
* the port uses the closed form of the same truncated integral,
30
*
31
* int_0^X x mu e^{-mu x} dx = (1 - e^{-muX}(1 + muX))/mu,
32
* int_0^X mu e^{-mu x} dx = 1 - e^{-muX}, muX = 20,
33
*
34
* which is the value MATLAB's quadrature converges to, so the two agree to
35
* MATLAB's RelTol of 1e-8 and in practice to round-off. The truncation at 20
36
* mean service times is kept: dropping it would change the answer in the
37
* eighth digit and no longer reproduce the reference.
38
*
39
* The general path (some cs != 1) is MATLAB's class-based preemptive-priority
40
* surrogate, with the classes sorted by decreasing mean size. Note that it
41
* ignores cs entirely -- the residual term is sum_{i<=k} lambda_i/mu_i^2,
42
* i.e. the second moment of an exponential -- so for a non-exponential input
43
* the reference answer does not depend on the variability it was given. That
44
* is a defect of the reference, reproduced here rather than silently fixed.
45
*
46
* ARITHMETIC. exp appears in the exponential path, so the function is gated.
47
*/
48
49
#include <algorithm>
50
#include <cstddef>
51
#include <numeric>
52
#include <vector>
53
54
#include "
line/api/qsys/qsys_mg1_setf.h
"
55
#include "
line/api/qsys/qsys_types.h
"
56
#include "
line/num/number.h
"
57
#include "
line/util/error.h
"
58
59
namespace
line
{
60
namespace
qsys
{
61
62
/**
63
* @brief M/G/1 under LRPT (longest remaining processing time).
64
*
65
* @param lambda per-class arrival rates
66
* @param mu per-class service rates
67
* @param cs per-class coefficients of variation of the service time
68
*/
69
template
<
class
T>
70
Mg1DisciplineResult<T>
qsys_mg1_lrpt
(
const
std::vector<T>& lambda,
const
std::vector<T>& mu,
71
const
std::vector<T>& cs) {
72
static_assert
(
num_traits<T>::has_transcendental
,
73
"qsys_mg1_lrpt requires transcendental arithmetic"
);
74
const
T rho_total = detail::mg1_discipline_check(
"qsys_mg1_lrpt"
, lambda, mu, cs);
75
const
std::size_t K = lambda.size();
76
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
77
const
T two =
num_traits<T>::from_int
(2);
78
const
T cs_tol = T(
num_traits<T>::from_double
(1e-6));
79
80
bool
all_exp =
true
;
81
for
(std::size_t i = 0; i < K; ++i)
82
if
(!(
num_abs
(T(cs[i] - one)) < cs_tol)) all_exp =
false
;
83
84
Mg1DisciplineResult<T>
r;
85
r.
W
.assign(K, zero);
86
87
if
(all_exp) {
88
T lambda_total = zero;
89
for
(
const
T& v : lambda) lambda_total += v;
90
T E_X2 = zero;
91
for
(std::size_t i = 0; i < K; ++i)
92
E_X2 += (lambda[i] / lambda_total) * two / (mu[i] * mu[i]);
93
const
T flat = lambda_total * E_X2 / (two * (one - rho_total) * (one - rho_total));
94
// muX = 20 for every class, so the two truncation factors are shared.
95
const
T e20 = detail::num_exp(T(
num_traits<T>::from_int
(-20)));
96
const
T mass = one - e20;
97
const
T first = one - e20 *
num_traits<T>::from_int
(21);
98
for
(std::size_t k = 0; k < K; ++k)
99
r.
W
[k] = (first / mu[k]) / (one - rho_total) + flat * mass;
100
}
else
{
101
// Classes sorted by decreasing mean service time; MATLAB's sort is
102
// stable, so ties keep the original class order.
103
std::vector<std::size_t> idx(K);
104
std::iota(idx.begin(), idx.end(), std::size_t(0));
105
std::stable_sort(idx.begin(), idx.end(),
106
[&](std::size_t i, std::size_t j) { return one / mu[j] < one / mu[i]; });
107
T rho_prev = zero, E_R_k = zero;
108
for
(std::size_t pos = 0; pos < K; ++pos) {
109
const
std::size_t k = idx[pos];
110
const
T rho_curr = rho_prev + lambda[k] / mu[k];
111
E_R_k += lambda[k] / (mu[k] * mu[k]);
112
r.
W
[k] = E_R_k / ((one - rho_prev) * (one - rho_curr)) + one / mu[k];
113
rho_prev = rho_curr;
114
}
115
}
116
r.
rhohat
= detail::mg1_discipline_rhohat(lambda, r.
W
);
117
return
r;
118
}
119
120
}
// namespace qsys
121
}
// namespace line
122
123
#endif
// LINE_API_QSYS_QSYS_MG1_LRPT_H
error.h
The exception types the port throws.
line::qsys
Definition
qsys_bmapm1.h:58
line::qsys::qsys_mg1_lrpt
Mg1DisciplineResult< T > qsys_mg1_lrpt(const std::vector< T > &lambda, const std::vector< T > &mu, const std::vector< T > &cs)
M/G/1 under LRPT (longest remaining processing time).
Definition
qsys_mg1_lrpt.h:70
line
Definition
aoi_dist2ph.h:52
line::num_abs
T num_abs(const T &v)
Definition
number.h:172
number.h
Number-type abstraction for the templated API port.
qsys_mg1_setf.h
M/G/1 under SETF (shortest elapsed time first), the non-preemptive counterpart of FB/LAS.
qsys_types.h
Shared return type and arithmetic helpers for the templated qsys port.
line::num_traits
Definition
number.h:111
line::qsys::Mg1DisciplineResult
Definition
qsys_mg1_setf.h:55
line::qsys::Mg1DisciplineResult::W
std::vector< T > W
per-class mean response time
Definition
qsys_mg1_setf.h:56
line::qsys::Mg1DisciplineResult::rhohat
T rhohat
Q/(1+Q) with Q = sum_k lambda_k W_k.
Definition
qsys_mg1_setf.h:57
include
line
api
qsys
qsys_mg1_lrpt.h
Generated by
1.18.0