umbotВсе тесты проводились на чистой установке без сетевых вызовов, внешних API или пользовательской логики.
Измерялось только время выполнения внутренней логики фреймворка: от получения запроса до формирования объекта ответа.
Ниже приведены результаты тестирования времени выполнения только внутренней логики фреймворка,
исключая время выполнения action. Тесты проводились на системе с AMD Ryzen 5 5600G, 32 ГБ ОЗУ, SSD.
Примечание: Время первичной загрузки новых изображений или аудиофайлов (когда
tokenдля файла ещё не кэширован в БД) может значительно превышать 1 секунду и выделено отдельно. После первой загрузки и кэширования, последующие обращения к тем же файлам выполняются быстро.
Особое внимание уделено оптимизации работы с регулярными выражениями (RegExp). При использовании isPattern: true,
umbot кэширует скомпилированные RegExp объекты.
Это означает, что при первом вызове run() с командами, использующими новые паттерны, происходит компиляция RegExp, что занимает больше времени.
При последующих вызовах с теми же паттернами, скомпилированные RegExp берутся из кэша, что значительно ускоряет выполнение.
Также в фреймворке предусмотрена группировка регулярных выражений из разных команд.
То есть когда задано множество команд с регулярными выражениями, эти регулярные выражения объединяются в группы, для
уменьшения количества обращений.
Стенд benchmark/comparison/ сравнивает umbot с реальными фреймворками
конкурентов по платформам. У стенда собственный
benchmark/comparison/package.json (зависимости-конкуренты не попадают в
package.json репозитория). Каждый стенд — отдельный файл: сценарии, lockstep,
метрики и печать общие (runner.js), файл платформы задаёт вход и подключает
конкурентов. Запуск:
npm i --prefix benchmark/comparison
npm run compare # Telegram: umbot vs grammy, telegraf
npm run compare:alisa # Alisa: umbot vs yandex-dialogs-sdk
npm run compare:vk # VK: umbot vs vk-io (+ @vk-io/hear)
npm run compare:viber # Viber: umbot vs viber-bot
npm run compare:max # MAX: umbot vs @maxhub/max-bot-api
Файлы стендов устроены одинаково (telegram.js, alisa.js, vk.js,
viber.js, max.js): makeUpdate — апдейт платформы, umbot — адаптер
и botType, competitors() — офлайн-путь каждого конкурента (обработка через
штатный handleUpdate/handleRequest/handleWebhookUpdate, без сети).
Добавление нового конкурента: пакет — в dependencies
benchmark/comparison/package.json, реализация — в competitors() файла
платформы по образцу существующих.
Дополнительно в benchmark/comparison/:
stress.js (запуск: npm run stress:compare:vk и т.п.,
или node stress.js <файл> [сек] [окно]) — поведение под непрерывным
потоком: окно 200 запросов in-flight, 30 с, трафик-микс
(точное/частичное/регулярка/fallback = 40/25/25/10), ротация 1000
user_id, 1000 команд. Метрики: агрегатный RPS под потоком (не путать
с 1000/p50 тепличного стенда — другой режим), p50/p95/p99, доля
времени в GC, тренд пола кучи (пила мусора — норма, рост пола —
проблема), retained-дельта после финального GC (утечка). Каждый
участник — в изолированном дочернем процессе: падение одного по heap
limit роняет только его строку.baseline.js для CI night-job:
npm run baseline:update — записать p50 umbot на маркерном сценарии
«50 команд, точное совпадение» всех пяти стендов;
npm run baseline:check — свериться (допуск +15% JIT-шума; exit 1 при
регрессии). Ловит «кто-то занёс тяжёлый RegExp-компайл в горячий путь»
автоматикой, а не ручным прогоном. Baseline-файл создаётся на той же
машине, где гоняется check.Честность стенда (едина для всех платформ):
handleUpdate у grammy/telegraf/max-bot-api,
handleRequest у yandex-dialogs-sdk, handleWebhookUpdate у vk-io,
_handleEventReceived у viber-bot).
botInfo конкурентам задаётся явно (документированная опция/свойство),
иначе их handleUpdate ходит за getMe в сеть.ctx.skipAutoReply = true
(без него его getContent ушёл бы в API платформы — мерился бы интернет);
у конкурентов ctx.reply/ctx.send не вызывается. VkAdapter в стенде
работает с vk_load_user_info: false — иначе адаптер по умолчанию ходит
в VK API за именем пользователя на каждое сообщение.hears(new RegExp(...))/аналог (единая обёртка asRegExp в runner.js;
до выравнивания их hears(строка) матчит только точное совпадение —
проверено по исходникам и поведенческим тестом на каждой библиотеке:
grammy — txt === t, telegraf и max-bot-api — new RegExp('^…$'),
@vk-io/hear — text === condition, viber-bot строковых триггеров не имеет).
RegExp-вариант — их честная цена той же семантики, что у umbot-слота.Результаты ниже — прогон от сентября 2026 (методика v4). Окружение: AMD Ryzen 5 5600G
(6 ядер / 12 потоков), 32 ГБ ОЗУ, Windows 10, Node.js 24.15.0, без re2. Версии конкурентов
зафиксированы в benchmark/comparison/package-lock.json: grammy 1.46.0, telegraf 4.16.3,
yandex-dialogs-sdk 2.3.0, vk-io 4.10.1 (+ @vk-io/hear 1.1.1), viber-bot 1.0.18,
@maxhub/max-bot-api 0.3.0.
1000 / p50 для одного ядра, теоретический потолок
роутинга, а не пропускная способность сервера. «666 667 RPS» означает «роутинг занимает 1,5 мкс»,
а не «сервер обслужит 666 тысяч запросов в секунду».bot.hears(...), аналоги у остальных). В grammY/telegraf каждый такой обработчик — отдельный
слой middleware, апдейт проходит слои по очереди. Поэтому их стоимость растёт линейно с
числом команд, а у umbot точное совпадение ищется в хэш-таблице за константу. Если в grammY
написать один общий обработчик со своим Map, разрыв на больших базах пропадёт — стенд
сравнивает встроенный роутинг, а не «лучший возможный код на каждом фреймворке».npm run build
npm i --prefix benchmark/comparison
npm run compare # Telegram (grammy, telegraf); ещё: compare:alisa / compare:vk / compare:viber / compare:max
npm run stress:compare # стресс под потоком; ещё: stress:compare:alisa / :vk / :viber / :max
node benchmark/comparison/stress.js telegram.js 10 2000 # окно 2000 одновременных запросов
Абсолютные цифры на другой машине будут другими; соотношения и форма кривой («растёт ли время с числом команд») должны совпасть. Если у вас выходит иначе — это повод открыть issue с выводом стенда.
| Сценарий (найденная команда — в середине списка) | Реализация | Время p50, мкс | p95, мкс | RPS (1000/p50) | Память, КБ/запр |
|---|---|---|---|---|---|
| 6 команд, точное совпадение | umbot | 1.4 | 2.2 | 714 286 | 4.9 |
| grammy | 3.1 | 6.7 | 322 581 | 13.2 | |
| telegraf | 3.8 | 8.5 | 263 158 | 15.9 | |
| 6 команд, регулярка | umbot | 1.5 | 2.8 | 666 667 | 4.7 |
| grammy | 2.7 | 5.2 | 370 370 | 13.2 | |
| telegraf | 3.5 | 6.9 | 285 714 | 15.8 | |
| 50 команд, точное совпадение | umbot | 1.5 | 3.4 | 666 667 | 4.7 |
| grammy | 14.1 | 26.7 | 70 922 | 76.3 | |
| telegraf | 17.6 | 29.7 | 56 818 | 97.4 | |
| 50 команд, частичное совпадение | umbot | 2.7 | 4.9 | 370 370 | 4.7 |
| grammy | 14.0 | 26.0 | 71 429 | 76.4 | |
| telegraf | 17.9 | 30.2 | 55 866 | 97.3 | |
| 50 команд, регулярка | umbot | 2.9 | 5.6 | 344 828 | 4.7 |
| grammy | 14.2 | 25.4 | 70 423 | 76.4 | |
| telegraf | 18.5 | 33.7 | 54 054 | 97.2 | |
| 50 команд, fallback | umbot | 3.8 | 7.3 | 263 158 | 4.7 |
| grammy | 23.6 | 39.2 | 42 373 | 115.8 | |
| telegraf | 29.7 | 52.4 | 33 670 | 27.5 | |
| 500 команд, точное совпадение | umbot | 1.5 | 2.3 | 666 667 | 4.7 |
| grammy | 137.6 | 225.2 | 7 267 | 40.5 | |
| telegraf | 170.6 | 279.1 | 5 862 | 127.7 | |
| 500 команд, частичное совпадение | umbot | 10.9 | 21.5 | 91 743 | 4.8 |
| grammy | 142.2 | 235.5 | 7 032 | 39.2 | |
| telegraf | 169.1 | 278.7 | 5 914 | 127.7 | |
| 500 команд, регулярка | umbot | 35.3 | 48.7 | 28 329 | 4.8 |
| grammy | 249.0 | 429.2 | 4 016 | 39.2 | |
| telegraf | 300.3 | 665.2 | 3 330 | 125.9 | |
| 1000 команд, точное совпадение | umbot | 2.1 | 4.2 | 476 190 | 4.7 |
| grammy | 450.6 | 767.7 | 2 219 | 73.9 | |
| telegraf | 505.9 | 800.7 | 1 977 | 118.5 | |
| 1000 команд, регулярка | umbot | 37.8 | 68.8 | 26 455 | 7.6 |
| grammy | 353.4 | 841.4 | 2 830 | 73.9 | |
| telegraf | 462.8 | 917.6 | 2 161 | 118.5 | |
| 1000 команд, частичное совпадение | umbot | 31.4 | 62.3 | 31 847 | 4.9 |
| grammy | 334.1 | 668.5 | 2 993 | 73.9 | |
| telegraf | 423.0 | 831.6 | 2 364 | 118.5 | |
| 1000 команд, fallback (неизвестная фраза) | umbot | 45.6 | 91.5 | 21 930 | 4.8 |
| grammy | 587.4 | 1532.9 | 1 702 | 30.1 | |
| telegraf | 820.6 | 1927.8 | 1 219 | 100.6 |
Как читать (включая неудобное):
node benchmark/comparison/stress.js telegram.js 10 2000,
1000 команд, 10 с): umbot — 74 031 RPS, p99 33 мс, куча 15 МБ; grammy — 518 RPS, p99 6.6 с,
куча ~1.9 ГБ; telegraf — 292 RPS, p99 12 с, куча ~3.1 ГБ (близко к лимиту кучи Node по
умолчанию ~4 ГБ).Те же сценарии и правила (методика v4). Ключевые числа — время p50 на апдейт; полный вывод печатает каждый стенд:
| Конкурент | Счёт umbot | 6 команд, точное | 1000 команд, точное | 1000 команд, регулярка | Память на апдейт |
|---|---|---|---|---|---|
| yandex-dialogs-sdk | 13 побед из 13 | 1.9 против 4.5 мкс | 1.6 против 142.2 мкс | 35.6 против 222.8 | umbot меньше везде (4.1–7.6 против 7.5–112) |
| vk-io | 7 побед, 5 паритетов, 1 поражение | 1.5 против 1.2 мкс | 1.2 против 24.8 мкс | 53.7 против 72.5 | 6 команд: vk-io меньше (3.3 против 5.2); с 50 команд umbot меньше (до 100 КБ у vk-io) |
| viber-bot | 13 побед из 13 | 1.5 против 1.9 мкс | 3.5 против 210.9 мкс | 66.5 против 259.3 | до 50 команд viber-bot меньше (1.5–3.8 против 4.6–4.9); с 500 команд umbot меньше |
| max-bot-api | 11 побед, 2 паритета | 1.4 против 1.3 мкс | 1.2 против 131.9 мкс | 36.3 против 153.3 | umbot меньше везде (4.4–7.2 против 6.3–117) |
Как читать (включая неудобное):
Promise.all по
всем командам на каждый запрос ради подбора «похожих» фраз (проверено по исходникам), отсюда
рост с числом команд. SDK не обновлялся с 2022 года.Стресс-стенд (stress.js) меряет не латентность отдельного запроса, а поведение под потоком:
окно 200 запросов in-flight, 30 с, микс 40/25/25/10 (точное/частичное/регулярка/fallback),
1000 команд (намеренно тяжёлый сценарий), ротация 1000 разных user_id. Каждый участник — в
отдельном процессе:
| Стенд | Участник | Запросов | RPS | p50 | p99 | GC, % времени | Утечка |
|---|---|---|---|---|---|---|---|
| VK | umbot | 2 263 000 | 75 420 | 2.4 мс | 4.6 мс | 0.3 | нет (0.10*) |
| vk-io | 960 706 | 31 985 | 3.2 мс | 7.1 мс | 2.7 | нет | |
| Telegram | umbot | 2 355 000 | 78 487 | 2.4 мс | 4.3 мс | 0.3 | нет (0.12*) |
| grammy | 11 136 | 370 | 515 мс | 905 мс | 64.0 | нет | |
| telegraf | 7 664 | 253 | 747 мс | 1 467 мс | 66.0 | нет | |
| Alisa | umbot | 2 076 800 | 69 190 | 2.6 мс | 5.1 мс | 0.3 | нет (0.17*) |
| dialogs-sdk | 82 112 | 2 734 | 67 мс | 105 мс | 16.3 | нет | |
| Viber | umbot | 2 210 400 | 73 654 | 2.5 мс | 4.6 мс | 0.3 | нет (0.13*) |
| viber-bot | 200 986 | 6 698 | 15 мс | 31 мс | 0.3 | нет | |
| MAX | umbot | 2 259 800 | 75 290 | 2.5 мс | 4.5 мс | 0.3 | нет (0.11*) |
| max-bot-api | 22 285 | 742 | 253 мс | 373 мс | 65.6 | нет |
* КБ на 1000 запросов: постоянный прогретый набор (JIT, кэш регулярок групп) — суммарно ~260 КБ и за 5 с, и за 20 с потока, от длительности не зависит.
Исправление методики (3.1.0). В прежней версии стенда retained-память мерилась, пока массив задержек самого воркера (8 байт на запрос) был ещё жив. «Утечка» получалась пропорциональной числу запросов и штрафовала самого быстрого участника: у umbot выходило 8–10 КБ/1000 запросов, у telegraf (177 КБ/1000) и max-bot-api (57 КБ/1000) — тоже артефакт. Квантили теперь считаются до замера, массив освобождается; после исправления утечек нет ни у одного участника. Цифры в таблице — прогон после исправления.
Как читать (включая неудобное):
* базовый контроллер umbot на промахе отвечает пользователю, то есть
делает сетевой вызов в API платформы. Стенд регистрирует * с skipAutoReply, чтобы мерить
роутинг, а не интернет. В проде промах без обработчика — это реальный запрос к платформе.Время первых 200 запросов после регистрации команд, без прогрева (важно для Cloud Functions: первый запрос после подъёма инстанса). Холодный JIT платит первый запрос в десятки раз дороже плато; участники стартуют в одинаковых условиях:
| Платформа | Сценарий | umbot: 1-й / ср. 200, мкс | Конкурент: 1-й / ср. 200, мкс |
|---|---|---|---|
| Telegram | 6 команд, точное | 1 364 / 14.0 | grammy 688 / 14.8 |
| Telegram | 1000 команд, точное | 193 / 7.3 | grammy 1 806 / 422.9 |
| Alisa | 6 команд, точное | 1 540 / 16.4 | dialogs-sdk 905 / 15.2 |
| VK | 6 команд, точное | 1 659 / 19.9 | vk-io 634 / 7.5 |
| VK | 1000 команд, точное | 124 / 8.6 | vk-io 523 / 59.5 |
| Viber | 6 команд, точное | 1 277 / 14.0 | viber-bot 581 / 7.5 |
| MAX | 6 команд, точное | 1 326 / 13.3 | max-bot-api 433 / 11.7 |
Как читать: на малых базах первый запрос у umbot в 1.7–3 раза медленнее, чем у всех конкурентов (1.3–1.7 мс против 0.4–0.9 мс) — у umbot больше кода, который V8 компилирует при первом вызове. Для serverless это ~1 мс на холодный старт инстанса. С ростом числа команд umbot и в холодном старте впереди: точное совпадение не требует прогрева.
| Конкурент | Тепличный стенд (13 сценариев) | Стресс, 1000 команд (RPS) | Память на апдейт |
|---|---|---|---|
| grammy (Telegram) | umbot быстрее во всех 13 | 78 487 против 370 (212×) | umbot меньше везде (в 2.7–25 раз) |
| telegraf | umbot быстрее во всех 13 | 78 487 против 253 (310×) | umbot меньше везде (в 3.2–27 раз) |
| yandex-dialogs-sdk | umbot быстрее во всех 13 | 69 190 против 2 734 (25×) | umbot меньше везде |
| vk-io | 7 побед, 5 паритетов, 1 поражение | 75 420 против 31 985 (2.4×) | на 6 командах vk-io меньше, дальше umbot |
| viber-bot | umbot быстрее во всех 13 | 73 654 против 6 698 (11×) | до 50 команд viber-bot меньше, дальше umbot |
| max-bot-api | 11 побед, 2 паритета (6 команд) | 75 290 против 742 (101×) | umbot меньше везде |
Где umbot уступает или идёт вровень:
Где umbot стабильно лучше:
Практический вывод: на малых ботах (до ~50 команд) решает не быстродействие — разница в долях микросекунды не видна на фоне сетевого круга платформы в 50–300 мс, — а функциональность (мультиплатформенность, шаги, NLU, state). На ботах с сотнями команд и высоким потоком у umbot кратный запас по CPU и памяти.
Честная оценка границ инструмента (а не только его сильных сторон):
umbot создан для:
addStep), формы-опросники (addForm);Сценарии, где потребуется дополнительная логика на вашей стороне:
bot.addEvent('photo' | 'voice' | 'callback' | 'inline' | ...), 17
универсальных типов, адаптеры платформ объявляют поддержку через
supportedEvents (валидация и предупреждения при подключении).
Доп. код потребуется только там, где нужна глубокая платформенная
специфика события (например, бизнес-логика по полям, которые нет смысла
унифицировать) — читайте controller.eventType, controller.payload
и requestObject прямо в хендлере события;bot.addEvent(...) +
кросс-платформенный API-фасад ctx.api?.sendPhoto/sendDocument/ sendAudio/sendVideo/answerCallback (с матрицей поддержки по платформам
и can(method), см. api-reference.md). Однако полный охват Bot API
(web-app, payments, games, бизнес-режимы и прочий длинный хвост методов)
остаётся за TelegramRequest — вызовы собираются вручную через
API-клиент;bench-вывод), либо включите re2 для регулярных выражений.Ключевая мысль: umbot — не «универсальный фреймворк на все случаи», а инструмент для сценария «диалоговый бот/навык на многих платформах с предсказуемыми ресурсами». Если ваш продукт — диалоги, команды и шаги, вы получаете всё из коробки и производительность выше конкурентов. Если ваш продукт — обработка медиа-потоков или тонкая Telegram-специфика, взвесьте: дополнительный код на umbot против отказа от 6 других платформ и ресурсной эффективности.
Бенчмарки bench и stress перед запуском проверяют, хватит ли памяти.
Проверка учитывает три реальных ограничения: лимит heap V8 (--max-old-space-size или ~4 ГБ
по умолчанию — даже при 16 ГБ RAM Node не аллоцирует больше), cgroup-лимит (docker/k8s) и
физическую память с учётом page cache (ядро сбрасывает кэш при нехватке — os.freemem()
на unix занижает доступное, из-за чего тест ложно отказывался в запуске). Оценка потребления
основана на фактических замерах: ~466 Б на строковую команду, ~0.8 КБ на isPattern-команду,
~1.6 КБ с учётом фрагментации V8 кучи при экстремальных количествах.
| Команда | Описание |
|---|---|
npm run bench |
Время обработки команд с разной сложностью regex (9 уровней от 50 до 1 000 000 команд) |
npm run stress |
Стресс-тест: полная нагрузка (1003 команды, параллельные запросы, burst-тесты, максимальный RPS за 15 сек) |
npm run stress:lite |
Облегчённый стресс-тест (8 команд вместо 1003). Подходит для быстрой проверки на слабом оборудовании |
npm run stress:long |
Долгосрочный тест на 48 часов. Проверяет утечки памяти и стабильность производительности |
npm run comparison |
Честное сравнение umbot против «чистой» реализации роутера (lockstep, медианы; свой роутер — через UM_COMPARISON_ROUTER) |
npm run compare |
Сравнение с реальными фреймворками: compare — Telegram (grammy, telegraf); compare:alisa — Alisa (yandex-dialogs-sdk); compare:vk — VK (vk-io); compare:viber — Viber (viber-bot); compare:max — MAX (max-bot-api). Одинаковый вход, p50/p95/IQR/RPS/память + cold-start (см. выше) |
npm run stress:compare:* |
Стресс-стенд против конкурентов: stress:compare — Telegram; stress:compare:alisa/vk/viber/max — другие платформы. 30 с непрерывного потока (окно 200 in-flight, микс 40/25/25/10, 1000 команд, 1000 user_id), агрегатный RPS, доля GC, тренд кучи, retained-утечка. Каждый участник в изолированном процессе |
npm run baseline:update / baseline:check |
Регрессионный контроль для CI night-job: p50 umbot на маркерном сценарии всех 5 стендов против baseline.json (допуск +15%); check возвращает exit 1 при регрессии |