Русский     English

Моделирование ограниченной очереди в VisualAivika

Автор: Давид Сорокин

Дата публикации: 6 августа 2025 года

В этой статье я собираюсь показать, как моделировать в VisualAivika очереди ограниченной ёмкости. Такая задача может часто встречаться при моделировании систем массового обслуживания (СМО). Помимо этого, VisualAivika поддерживает и системную динамику, в рамках которой можно интегрировать системы обыкновенных дифференциальных уравнений (ОДУ). Более того, системы ОДУ и системы СМО можно комбинировать в рамках одной модели. Однако сейчас речь пойдет об одной задаче — как моделировать очереди ограниченной ёмкости.

Представим себе следующую задачу. По компьютерной сети устройству приходят пакеты в случайном порядке. Допустим, что задержка пакетов имеет показательное распределение со средним 5 единиц модельного времени. Устройство имеет очередь ограниченной ёмкости, скажем, в 7 единиц. Если очередь заполнена, то пакет теряется. Внутри устройства пакеты обрабатываются друг за другом. Время обработки одного пакета имеет равномерное целочисленное распределение в пределах от 0 до 6 единиц модельного времени. Нужно оценить количество потерянных пакетов через 10000 единиц модельного времени.

Итак, у нас есть поток входящих пакетов. В VisualAivika это будет поток внешних событий, где задержка между приходом событий подчиняется некоторому случайному распределению. По условиям задачи задержка имеет показательное распределение со средним 5 единиц.

Packets = Stream.exponential(5);

Мы считаем, что при обработке пакетов есть предельная ёмкость очереди, которую зададим как внешний параметр.

Capacity = 7;

Для блокировки обработки пакетов нам понадобится ресурс, который может обрабатывать не более одного пакета за раз. Такой вид ресурсов называется еще прибором.

Resource = Facility.create();

Для ограничения ёмкости будем учитывать статистику по пакетам, находящимся в очереди ресурса на обработку. При обработке заявок (транзактов) эта статистика будет обновляться автоматически. Тогда мы сможем запросить фактическую длину очереди через вызов функции Facility.queueLength(Resource).

Нам, конечно, интересно количество отказов. Заведем статистику и для этого. Позднее мы сможем построить графики по такой статистике.

FailingQueue = Queue.create();

Теперь определим обработку пакетов (заявок, транзактов). Входящие заявки будут поступать на блок, который будет решать, а не превышена ли ёмкость ресурса? В зависимости от этого пакеты будут перенаправлены на соответствующие блоки.

Chain1Init =
  Block.select(if Facility.queueLength(Resource) < Capacity
               then Chain2Proceed
               else Chain3Fail);

В редакторе уравнений переносы каретки придется заменить на пробелы, а знаки точки с запятой (в конце) вводить не нужно. На рисунке 1 показано, как это должно выглядеть в редакторе уравнений.

Рис 1. В редакторе уравнений VisualAivika выражение не имеет символа переноса каретки (версия для Linux).

Если ёмкость ресурса позволяет, то ставим пакет в очередь на обработку, где возможна блокировка всей цепочки, пока имитируем активность для обработки пакета. Здесь мы захватываем ресурс, затем имитируем активность, а после освобождаем ресурс для других пакетов.

Chain2Proceed =
  Block.seize(Resource) >>>
  Block.advance(randomInt(0, 6)) >>>    // это задержка
  Block.release(Resource) >>>
  Block.terminate;

Иначе, если ёмкость ресурса заполнена, то считаем, что пакет теряется, но статистику все равно собираем, чтобы потом посмотреть ее на графиках, например, на графике отклонения (тренд + доверительные интервалы).

Chain3Fail =
  Block.queue(FailingQueue) >>>
  Block.depart(FailingQueue) >>>
  Block.terminate;

Теперь у нас есть все, чтобы запустить имитацию модели. Берем поток внешних событий и запускаем их обработку заданной цепочкой блоков.

Runner = do! Block.runByStream(Packets, Chain1Init);

Все введенные элементы можно уместить на одной диаграмме, как показано на рисунке 2. Пусть это будет диграмма под названием Model.

Рис 2. Элементы на одной диаграмме.

Настало время для вывода графиков. Чтобы в VisualAivika посмотреть их, сначала нужно сформировать показатели. Показатели сами дискретно-событийные, но при переходе на графики и таблицы часть из них немного огрубляется (дискретизируется), но зато это позволяет использовать их в тех же системах обыкновенных дифференциальных уравнений (системная динамика).

Создадим новую диаграмму Results и определим там следующие элементы.

// общее количество отказов
FailCount = Queue.enqueueCount(Model.FailingQueue);

// количество захватов ресурса
CaptureCount = Facility.captureCount(Model.Resource);

// время ожидания в очереди
WaitTime = Facility.waitTime(Model.Resource);

// дискретизированная длина очереди (для ресурса ограниченной ёмкости)
Len = Facility.queueLength(Model.Resource);

// фактическая статистика по длине очереди (для ресурса ограниченной ёмкости)
LenStats = Facility.queueLengthStats(Model.Resource);

Итак, по условию задачи конечное модельное время равно 10000. Ниже в графиках количество экспериментов задано как 1000, но я для начала рекомендую поставить значение 10.

Во-первых, убедимся, что наше ограничение по размеру длины очереди никогда не нарушается. Сначала выведем сводную статистику по длине очереди. Если показатель Len дискретизированный от величины длины очереди “в моменте модельного времени”, то показатель LenStats несет в себе информацию о фактических экстремумах. Ниже на рисунке 4 мы видим, что фактический верхний экстремум никогда не превышает значения, которое было задано в условиях задачи.

Рис 3. Сводная статистика по длине очереди.

В нашем примере процесс быстро становится стационарным. Поэтому ожидаемо, что оба приведенных показателя длины очереди (дискретизированная величина “в моменте” и фактическая статистика по истории наблюдений) сходятся на графике отклонения, который показывает тренды и доверительные интервалы по правилу 3-сигм.

Рис 4. График отклонения для длины очереди.

Значит, мы убедились, что инвариант сохранен. Тогда сразу покажем итоговый результат, а потом и остальные показатели, которые могут быть интересны.

График отклонения для числа потерянных пакетов показан на рисунке 5. Отрицательные значения на графике возникают из-за того, что применяется правило 3-сигм, то есть, это значения тренда за вычетом утроенного стандартного отклонения. Эти значения вполне могут быть отрицательными несмотря на то, что сама статистика по значениям неотрицательная.

Рис 5. График отклонения для количества потерянных пакетов.

На рисунке 6 ниже можем взгянуть на гистограмму, которая показывает распределение количество потерянных пакетов на момент окончания имитации по всем запускам таких имитаций.

Рис 6. Распределение количества потерянных пакетов в конце имитации.

Здесь, кстати, мы видим, что нет отрицательных значений в конечной точке моделирования, о чем мы могли бы подумать, глядя на график отклонения.

Это были ответы на исходную постановку задачи.

Теперь посмотрим на те пакеты, которые все же попали на обработку. Рисунок 7 ниже показывает график отклонения для времени ожидания в очереди для таких пакетов.

Рис 7. График отклонения для времени ожидания в очереди.

Процесс стал давно стационарным. Поэтому никаких сюрпризов. Более детальная информация по распределению показана на рисунке 8, что отображает статистическую сводку по времени ожидания пакетов в очереди на обработку.

Рис 8. Статистическая сводка для времени ожидания в очереди.

Ну, и, конечно, сложно представить ресурс прибора без информации о том, сколько раз такой прибор был захвачен, в данном случае, обрабатываемыми пакетами. Рисунок 9 дает такую информацию на графике отклонения, где видим восходящий тренд.

Рис 9. График отклонения для количества захватов прибора.

Случайные эффекты накладываются друг на друга, что в итоге дает нормальное распределение для количества захватов в конечной точке моделирования, что наглядно демонстрирует рисунок 10.

Рис 10. Распределение количества захватов прибора в конце имитации.

Итого мы взяли модель ограниченной очереди, запустили имитацию и получили ряд интересных графиков и таблиц.