Engee 文档
Notebook

群众服务理论:基本范例

让我们开始掌握分析研究离散事件逻辑和大众服务系统 (MSS) 模型的EventSimulation 库工具包。

问题描述

大众服务理论包括分析复杂系统性能的工具,这些工具可处理重复发生的随机事件,并可评估系统特征,如队列的存在服务开始前的平均等待时间平均设备停机时间

任何大规模服务系统的共同组成要素都是存在入站请求流、给定的系统结构(存储单元、队列和其他服务设备的数量和容量)、服务请求时间和服务规范(将请求分配给处理程序、从队列中选择请求和形成队列的算法)。

事件模拟库

通过EventSimulation 库,您可以对基于离散事件逻辑的系统进行编程。在这类系统中,事件之间的时间可以用相对值表示,仿真基于事件而不是明确定义的连续时间矢量,就像对由块创建的图形模型进行仿真一样。

首先,让我们在系统中安装这个库(此步骤只需执行一次,再次运行时注释掉下面一行)。

In [ ]:
Pkg.add( "EventSimulation" )

让我们考虑一种最简单的情况--有五个客户 "来到 "我们的系统,他们之间的时间间隔为 1 单位时间,为每个客户提供服务需要 1.5 单位时间

In [ ]:
using EventSimulation

function arrival(s)
    t = s.now
    println("$(s.now): Новый клиент")
    register!(s, x -> println("    $(x.now): Уход клиента, который пришел в $t"), 1.5)
end

s = Scheduler()

for t in 1.0:5.0
    register!(s, arrival, t)
end

go!(s)
1.0: Новый клиент
2.0: Новый клиент
    2.5: Уход клиента, который пришел в 1.0
3.0: Новый клиент
    3.5: Уход клиента, который пришел в 2.0
4.0: Новый клиент
    4.5: Уход клиента, который пришел в 3.0
5.0: Новый клиент
    5.5: Уход клиента, который пришел в 4.0
    6.5: Уход клиента, который пришел в 5.0

arrival 函数中,每个客户的到达时间都会被记录下来,一个离开事件会被创建并存储在调度程序 (Scheduler) 中,同时还有一个匿名函数,调度程序会在时间到时调用该函数。

x.now 的值取自调度器状态向量。

如果我们要分析无限数量的传入对象或事件,仅循环处理客户端是不够的。我们需要一个事件源,我们将使用repeat_register 创建该事件源。

In [ ]:
using EventSimulation

function arrival(s)
    t = s.now
    println("$(s.now): Новый клиент")
    register!(s, x -> println("    $(x.now): Уход клиента, который пришел в $t"), 1.5)
end

s = Scheduler()
repeat_register!(s, arrival, x -> 1.0)

go!(s, 7)
1.0: Новый клиент
2.0: Новый клиент
    2.5: Уход клиента, который пришел в 1.0
3.0: Новый клиент
    3.5: Уход клиента, который пришел в 2.0
4.0: Новый клиент
    4.5: Уход клиента, который пришел в 3.0
5.0: Новый клиент
    5.5: Уход клиента, который пришел в 4.0
6.0: Новый клиент
    6.5: Уход клиента, который пришел в 5.0
7.0: Новый клиент

客户仍然每 1 个时间单位到达一次,但现在他们的队列是无限的。go!(s, 7) 命令将模拟持续时间定义为 7 个时间单位。

让我们在模型中添加一个客户**计数器。为此,我们需要定义CounterState 结构,该结构将在调度程序Scheduler 创建时初始化。

需要注意的是,重新定义已创建的结构通常需要重新加载 Julia 内核(例如通过 GUI Engee)。

In [ ]:
using EventSimulation
using Random

mutable struct CounterState <: AbstractState
    count::Int
end

function arrival(s)
    function departure(x)
        x.state.count -= 1
        println("    $(round(x.now,digits=2)): Уход клиента, который пришел в $(round(t,digits=2)) (клиентов в очереди $(x.state.count + 1))" )
    end
    
    t = s.now
    println( "$(round(s.now,digits=2)): Новый клиент (клиентов в очереди $(s.state.count+1))")
    register!( s, departure, 1.0 )
    s.state.count += 1
end

s = Scheduler( CounterState(0) )

Random.seed!(1)
repeat_register!(s, arrival, x -> rand())
go!(s, 5)
0.07: Новый клиент (клиентов в очереди 1)
0.42: Новый клиент (клиентов в очереди 2)
    1.07: Уход клиента, который пришел в 0.07 (клиентов в очереди 2)
1.12: Новый клиент (клиентов в очереди 2)
    1.42: Уход клиента, который пришел в 0.42 (клиентов в очереди 2)
1.75: Новый клиент (клиентов в очереди 2)
    2.12: Уход клиента, который пришел в 1.12 (клиентов в очереди 2)
2.66: Новый клиент (клиентов в очереди 2)
    2.75: Уход клиента, который пришел в 1.75 (клиентов в очереди 2)
2.86: Новый клиент (клиентов в очереди 2)
3.63: Новый клиент (клиентов в очереди 3)
    3.66: Уход клиента, который пришел в 2.66 (клиентов в очереди 3)
    3.86: Уход клиента, который пришел в 2.86 (клиентов в очереди 2)
4.41: Новый клиент (клиентов в очереди 2)
    4.63: Уход клиента, который пришел в 3.63 (клиентов в очереди 2)

M/M/1 系统

最后,我们用以下参数实现一个经典系统:

  • 输入的请求流呈泊松分布
  • 服务时间的分布是指数分布
  • 服务服务器/请求处理程序的数量等于 1
  • 服务队列是无限的、
  • 服务纪律称为 FCFS(FIFO),取自 "先进先出 "或 "先进先出"。

为了安全起见,让我们把默认情况下可能不在系统中的库放在一起。

In [ ]:
Pkg.add( ["Distributions", "Statistics", "Random"] )

将它们安装到系统中后,我们可以使用以下算法模拟 M/M/1 LMS。

MMQueue 结构积累了调度员当前工作(在当前模拟过程中)的统计数据,并允许我们通过函数run! 的输出,获得在模拟1 000 000 时间单位内通过系统的每个特定对象 (mean( d-a )) 到达和离开时间之差的平均值。

In [ ]:
using EventSimulation
using Distributions
using Statistics
using Random

mutable struct MMQueue <: AbstractState
    sp::Exponential{Float64} # скорость обслуживания (service rate)
    qlen::Int   # длина очереди (queue length)
    busy::Bool  # занят ли обработчик (сервер)?
    arrival_times::Vector{Float64} # время когда клиент пришел
    departure_times::Vector{Float64} # время когда клиент ушел
end

# Событие "клиент пришел"
function arrival(s)
    push!(s.state.arrival_times, s.now)
    if s.state.busy # server is working?
        s.state.qlen += 1
    else
        s.state.busy = true
        register!(s, leave, rand(s.state.sp))
    end
end

# Событие "клиент покинул систему"
function leave(s)
    push!(s.state.departure_times, s.now)
    if s.state.qlen > 0 # есть ли ожидающие клиенты?
        s.state.qlen -= 1
        register!(s, leave, rand(s.state.sp))
    else
        s.state.busy = false
    end
end

# Запуск системы M/M/1 с заданным параметрами
#   ar: среднее время между появлениями клиентов
#   sr: среднее время, затраченное на обслуживание
function run(ar, sr)
    q = MMQueue( Exponential(1 / sr), 0, false, Float64[], Float64[] )
    s = Scheduler( q, Float64 )
    repeat_register!( s, arrival, x -> rand(Exponential(1/ar)) )
    go!( s, 1_000_000 )
    mean( d-a for (a,d) in zip(s.state.arrival_times, s.state.departure_times) )
end

# Запустим симуляцию системы с изменяемым средним
# временем между поступлением заявок на обработку
# (от 1/0.1 до 1/0.9 временных единиц)
plot()
arrival_rates = 0.1:0.1:0.9
rates = []

for seed in 1:20
    Random.seed!(seed)    
    r = [ abs(run(ar, 1.0)*(1-ar)-1) for ar in arrival_rates ]
    plot!( arrival_rates, r )
    append!( rates, r )
end

hline!( [0.03], c="red", ls=:dash, legend=false )
plot!( title="Среднее время обслуживания" )
Out[0]:

我们看到,在事件发生率为 1.11 的情况下,20 次运行中有 3 次超过了规定的平均服务时间**。

In [ ]:
count( last.(hcat(rates...)) .> 0.03 )
Out[0]:
3

更多示例可在库开发人员的资料库中找到:https://github.com/bkamins/EventSimulation.jl/tree/master/examples。

有关 EventSimulation 库的英文文档,请访问 https://bkamins.github.io/EventSimulation.jl/stable/。

结论

我们探讨了大规模维护系统建模的基本技术。Engee 允许在仿真循环中插入外部信息系统的调用或运行放置在画布上的模型,因此大规模维修理论工具可以成为系统可靠性分析链的重要组成部分。