Engee 文档
Notebook

排队理论:基本例子

让我们开始学习图书馆的工具 EventSimulation 用于离散事件逻辑和排队系统(QMS)模型的分析研究。

任务说明

排队理论包括用于分析复杂系统的工具,这些系统与重复发生的随机事件一起工作,并允许您评估系统特性,如队列开始维护前的平均等待时间,或**平均设

任何排队系统的常见组件是存在传入的请求流,给定的系统结构(存储设备,队列和其他服务设备的数量和容量),服务请求的时间和服务纪律(在处理程序之间分配请求的算法,从队列中选择请求并形成队列)。

事件模拟图书馆

图书馆 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 它取自dispatcher的状态向量。

如果我们想分析有无限数量的对象或事件来参加会议的情况,那么我们在一个循环中处理客户端是不够的。 我们将需要一个事件源,我们将使用它创建 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内核(例如,使用Engee GUI)。

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/1CFR。

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

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 ...

事件模拟图书馆的英文文件载于https://bkamins.github.io/EventSimulation.jl/stable /。

结论

我们研究了排队系统建模的基本技术。 Engee允许您将对外部信息系统的访问或运行放置在画布上的模型插入仿真循环中,因此排队理论工具可以成为系统可靠性分析链的重要组成部分。