English translation is not available yet. Showing Russian content.

Сравнить inference schedulers (FCFS vs Priority)

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Сравнить inference schedulers (FCFS vs Priority)

1. Цель задачи

Научиться проектировать и сравнивать два подхода к планированию инференса в многотенантной среде: First-Come-First-Served (FCFS) и Priority Queuing. Смоделировать двух тенантов с разными приоритетами, измерить latency (задержку) для high-priority запросов и убедиться, что priority scheduler снижает эту задержку на 80% по сравнению с FCFS.

Ключевой результат Экспериментально подтверждённый вывод о том, при каких условиях (нагрузка, размер burst) priority scheduler даёт выигрыш.


2. Исходные данные

Что нужноОткуда взять
Python окружение (3.10+)Локальная установка / Google Colab
asyncio (встроенный)Входит в стандартную библиотеку Python
Библиотеки для замеров и визуализацииtime, pandas, matplotlib
Понимание модели инференса LLMОписание в документации (время обработки ~ длина токенов)
Симуляция двух тенантовНаписать генератор запросов (Poisson / burst)

Если нет реального инструмента — симулируем:

  1. Создать класс InferenceRequest с полями: tenant_id, priority (0 — low, 1 — high), arrival_time, processing_time (симулируется как asyncio.sleep(random.expovariate(rate))).
  2. Генератор запросов: один генератор для high-priority (средняя интенсивность λ_high, burst-интервалы 5x), другой для low-priority (λ_low, постоянная нагрузка).
  3. Эмуляция GPU: один «воркер» (единое ядро asyncio), обрабатывающий по одному запросу за раз.

3. Технологический стек

КомпонентИнструментыНазначение
Язык симуляцииPython 3.10+Реализация schedulers и генераторов
Асинхронная очередьasyncio.Queue / asyncio.PriorityQueueБазовая структура для FCFS и Priority
Случайная генерацияrandom / numpyРаспределения времени между запросами и длительности обработки
Измеренияtime.perf_counterЗамер latency для каждого запроса
Анализpandas (опционально)Группировка метрик по тенантам
Визуализацияmatplotlib / seabornГрафики CDF latency и boxplot по приоритетам

4. Этапы выполнения

Этап 1: Создание базовой симуляции (1 час)

Действия

  1. Определить классы данных
    @dataclass
    class InferenceRequest:
        tid: int           # tenant id (0=low, 1=high)
        priority: int      # 0 (low) or 1 (high)
        arrival_time: float
        processing_time: float
        start_time: float = None
        finish_time: float = None
    
  2. Написать генератор запросов асинхронная функция request_generator(tid, priority, lam, sim_duration), которая каждые expovariate(lam) секунд создаёт запрос с processing_time = expovariate(1/mean_time). Параметр lam для high-priority тенанта сделать в 2 раза ниже, чем low, но добавить burst-режим – каждые 10 секунд генерировать 5 запросов подряд с задержкой 0.001.
  3. Реализовать класс Worker в нём метод run(queue: asyncio.Queue), который бесконечно берёт запрос из очереди, устанавливает start_time, await asyncio.sleep(processing_time), устанавливает finish_time, сохраняет в список завершённых.
  4. Запустить симуляцию с asyncio.Queue (FCFS): два генератора (high, low) + один воркер. Длительность симуляции 30 секунд. Собрать список завершённых запросов.

Ожидаемый результат этапа Работающий скрипт с FCFS scheduling, собирающий для каждого запроса: tenant, priority, arrival, start, finish, latency (finish - arrival).


Этап 2: Реализация Priority scheduler (1 час)

Действия

  1. Заменить asyncio.Queue на asyncio.PriorityQueue В PriorityQueue элементы должны быть кортежем (priority, arrival_time, request), где priority – целое число (0 – high, 1 – low). Воркер должен извлекать элемент с наименьшим приоритетом (т.е. high-priority запросы будут первыми вне зависимости от времени прибытия).
  2. Учесть, что обработка запроса невытесняющая – если начался low-priority запрос, он не прерывается. Priority выбирает следующий только после завершения текущего.
  3. Модифицировать генераторы для high-priority запросов использовать priority=0, для low – priority=1.
  4. Запустить ту же симуляцию (те же параметры, seed random) и собрать завершённые запросы.

Ожидаемый результат этапа Скрипт, который запускает ту же нагрузку с priority очередью, результаты записаны в отдельный CSV или список.


Этап 3: Сбор метрик и расчет (30 минут)

Действия

  1. Написать функцию compute_metrics(records, percentile=95):
    • Средняя, медианная, p95 latency для каждого tenant.
    • Пропускная способность (rps) по каждому tenant.
    • Доля запросов, не завершённых за время симуляции (drop rate – если очередь растёт бесконечно, но можно установить max queue size).
  2. Рассчитать относительное снижение (lat_high_fcfs - lat_high_priority) / lat_high_fcfs * 100%.
  3. Построить графики
    • CDF latency для каждого scheduler (два графика: один для high, один для low).
    • Boxplot сравнительный (FCFS vs Priority для high и low).
  4. Проверить целевой признак снижение latency high-priority >= 80%. Если нет – скорректировать параметры (увеличить нагрузку low-priority тенанта, уменьшить частоту high, изменить burst).

Ожидаемый результат этапа Таблица с метриками, графики, проверка гипотезы.


Этап 4: Анализ и отчёт (1 час)

Действия

  1. Сформулировать выводы
    • Насколько priority scheduler улучшил latency для high-priority? Насколько ухудшил для low-priority?
    • При какой нагрузке (коэффициент low/high) разница >80%?
    • Какие ограничения модели (non-preemptive, single worker) влияют на результат?
  2. Написать короткий markdown-отчёт (10–15 строк) с таблицей метрик, ключевым графиком и интерпретацией.
  3. Добавить раздел «Что изменится, если: preemptive, multiple workers, batching».

Ожидаемый результат этапа Готовый отчёт в файле report.md с ссылками на код, графики метрики.


Этап 5 (опционально): Ручное тестирование с разными параметрами (30 минут)

Действия

  1. Провести sweep по параметрам соотношение интенсивностей (λ_low / λ_high = 2, 5, 10), размер burst (3, 5, 10), длительность симуляции.
  2. Построить heatmap выигрыша в latency high-priority от параметров.
  3. Сформулировать рекомендации для production: при каком уровне загрузки (utilization) priority scheduler даёт значимый эффект.

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


5. Критерии приемки (Definition of Done)

  • Скрипт симуляции FCFS и Priority воспроизводится с фиксированным seed.
  • Каждый запрос имеет уникальный tid, arrival_time, processing_time, и метки времени старта/конца.
  • Для high-priority тенанта средняя или p95 latency при Priority scheduler снижена не менее чем на 80% по сравнению с FCFS при одинаковой нагрузке.
  • Построены и сохранены графики CDF и boxplot сравнения.
  • Результаты собраны в pandas DataFrame и сохранены в CSV.
  • Написан отчёт (markdown) с таблицей метрик и интерпретацией.
  • Код размещён в репозитории (GitHub Gist или папка) с инструкцией по запуску.

6. Ожидаемый результат

Основной артефакт Папка проекта, содержащая:

  • simulate.py – основной скрипт симуляции (с возможностью выбора scheduler через аргумент командной строки --scheduler fcfs|priority).
  • results/ – подпапка с CSV-файлами записей и PNG-графиками.
  • report.md – краткий отчёт с метриками и выводами.
  • requirements.txt – список зависимостей (можно пустой, если использует только стандартную библиотеку).

Дополнительные результаты

  • График CDF latency по приоритетам для FCFS и Priority.
  • Таблица сравнения p50, p95, средняя latency.
  • Вывод о том, какой scheduler рекомендуется для сценария с двумя тенантами разного приоритета.

7. Возможные сложности и их решение

СложностьРешение
Priority scheduler не даёт улучшенияУвеличить нагрузку low-priority (сделать λ_low больше, добавить burst для high). Проверить, что очередь FCFS не пуста (utilization >80%).
Симуляция слишком медленнаяСократить длительность до 20–30 секунд, использовать time.perf_counter вместо datetime.
Генерация random недетерминированнаяУстановить random.seed(42) и numpy.random.seed(42) до запуска.
Нужно считать latency только завершённыхОтфильтровать записи, где finish_time не None.
Графика нет на CIИспользовать matplotlib с агрессивным plt.savefig без показа окна.
PriorityQueue не поддерживает сравнение по кортежуИспользовать (priority, arrival_time, request) – встроенные кортежи Python корректно сравниваются лексикографически.

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Базовая симуляция (FCFS)1 ч
Этап 2: Priority scheduler1 ч
Этап 3: Сбор метрик и графики30 мин
Этап 4: Отчёт1 ч
Этап 5 (опционально): Sweep параметров30 мин
Итого4 ч (основной)

Примечание Для первого раза (незнакомый инструментарий) заложить +50% времени.


9. Связанные вопросы из базы знаний

ВопросТема
42Как работают очереди в asyncio?
87Разница между FCFS и Priority Queuing
153Эмуляция времени инференса LLM
204Сбор метрик latency в Python
311Построение CDF с matplotlib
422Статистические тесты для сравнения latency (Mann-Whitney)
509Влияние burst traffic на latency
611Невытесняющее планирование (non-preemptive)
720Multi-tenant архитектура AI сервисов
833Вычислительные ограничения GPU (batch size, VRAM)
899Использование asyncio.PriorityQueue

10. Чек-лист самопроверки

  • Я реализовал FCFS через стандартную asyncio.Queue и Priority через asyncio.PriorityQueue.
  • Для каждого запроса я правильно записал arrival_time, start_time, finish_time.
  • Я использовал общий seed для random (random.seed(42)), чтобы результаты были воспроизводимы.
  • Я проверил, что latency high-priority при FCFS значительно выше (хотя бы в 3 раза), чем при Priority.
  • Я построил минимум два графика (CDF и boxplot) и сохранил их.
  • Я рассчитал относительное снижение latency и убедился, что оно >=80%.
  • Я написал отчёт с таблицей метрик и выводами.
  • Я закоммитил код, результаты и отчёт в репозиторий.
  • Мой код работает без ошибок при повторном запуске.