Перейти к содержанию

Бенчмарки

Нормативная версия документации - английская

Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.

Фреймворк между клиентом NATS и обработчиком стоит времени на каждом сообщении: поток подписки, декодирование, диспетчеризация, ack. Эта страница говорит, сколько именно. Точкой отсчёта служит та же работа, написанная руками на async-nats.

Один процесс выполняет сценарий тремя способами. Сырой работает с async-nats напрямую. Адаптер вручную работает с типами этого крейта, без обработчика и без рантайма над ними. Сервис - это приложение, которое пишет пользователь. Всё остальное держится одинаковым: параметры соединения, подписка, конфигурация консьюмера, место подтверждения, декодирование в тот же тип, байты тела, рантайм tokio и сборка. Процедура принадлежит самому фреймворку и описана на странице бенчмарков RustStream; здесь опубликовано то, что она дала на этой машине.

Отсюда получаются две разницы. Адаптер против сырого клиента - это то, во что обходятся консьюмер и издатель этого крейта поверх клиента, который они оборачивают: число, за которое отвечает этот репозиторий. Сервис против сырого клиента - это то, во что обходится сервис целиком, адаптер и рантайм вместе. Стоимость самого рантайма - расстояние между двумя колонками, и она публикуется по каждому брокеру, потому что разная доля рантайма у разных брокеров - это факт о том, как транспорт и рантайм встречаются.

Числа

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

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

Core NATS - самое тонкое основание, на котором могут стоять эти числа: доставка там состоит из совпадения субъекта и тела, и сервер ничего не подтверждает, поэтому видно, что каждая колонка добавляет к предыдущей, без стоимости транспорта вокруг.

Строка с отметкой «неразличимо» - та, где половины расходятся меньше, чем разброс между прогонами каждой из них. Это честный исход везде, где транспорт стоит намного дороже диспетчеризации: каждая доставка JetStream несёт подтверждение обратно на сервер, и разница такого размера теряется внутри одного такого подтверждения. Число ниже шума прогонов читалось бы как точность, которой никто не измерял, поэтому оно не публикуется.

Строка с отметкой «упирается в брокер» - та, где транспорт заставляет консьюмера ждать сервер достаточно часто, чтобы на это ушла половина стоимости сообщения. Такое число говорит больше о сервере и локальной петле, чем об этом крейте. Core NATS не берёт с консьюмера ни одного круга на доставку: сервер сам проталкивает совпадение в подписку. Консьюмер JetStream pull берёт один запрос на пакет и подтверждение, которое отправляется без ожидания ответа. Круг, с которым их сравнивают, - это проба в блоке машины ниже, так что расчёт можно повторить.

Машинная форма того же прогона, из которой сайт фреймворка строит сводную таблицу по брокерам, лежит по адресу benchmarks/results.json.

Собственный код крейта

Вторая таблица подсчитана, а не засечена по часам: инструкции под callgrind и выделения памяти под DHAT. Каждый сценарий - сервис в том виде, в каком его пишет пользователь: он построен на NatsBroker, работает против того же стенда, что и таблица выше, и запущен на однопоточном рантайме. Все сценарии читают pull-консьюмер JetStream. Издатель в другом потоке наполняет стрим до начала выборки и ждёт, пока стрим подтвердит каждое сообщение: Core NATS ничего не хранит для консьюмера, который не читает, а стрим хранит.

Считается всё, что выполняет поток сервиса: фреймворк, этот крейт и работа клиента async-nats в этом потоке, где идёт задача его соединения. Поток издателя не считается, как и то, что ядро делает внутри системного вызова.

Инструкции и выделения даны на сообщение в установившемся режиме: это наклон между прогоном на 1000 доставок и прогоном на 2000. Последняя колонка - то, во что один раз обошёлся запуск сервиса: соединение, поиск стрима, создание консьюмера и первая доставка. Числа абсолютные, собственная цена фреймворка входит в них; отдельно её публикует ядро на своей странице бенчмарков.

Подсчёт немного зависит от того, какими порциями сокет отдаёт клиенту байты. За семь прогонов инструкции на сообщение сдвигались меньше чем на полпроцента, а выделения - на один блок в прогоне на 2000 доставок. just bench-code падает, если выделений больше наибольшего итога, который сценарий показывал, с запасом в десятую долю процента, и если инструкций больше чем на два процента против предыдущего прогона, сохранённого в target/ (или против базы, которую называет --baseline=main); запрос на слияние, который меняет цену, приводит свои числа.

Машина

Флаги сборки публикуются вместе с числами, потому что они эти числа меняют. Бинарник, собранный с -C target-cpu=native, даёт результат, который не воспроизвести на другой машине, поэтому рецепт очищает переменную перед сборкой.

Чего эти числа не значат

Это один консьюмер, один субъект, маленькое тело и сервер на локальной петле. Измеряется то, во что доставка обходится внутри этого крейта, а не то, сколько способен пронести NATS. Строку отсюда нельзя сравнивать со строкой, опубликованной для другого брокера: транспорты выполняют на сообщение разную работу.

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

Число JetStream снято на стриме в памяти с политикой work-queue. Так диск под сервером не попадает в измерение, которое посвящено диспетчеризации. Стрим на файловом хранилище отвечает на другой вопрос и отвечает про сервер, а не про этот крейт.

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

Как повторить

just bench

Рецепт поднимает стенд из docker-compose.test.yml, прогоняет оба сценария, гасит стенд и переписывает docs/benchmarks/results.json тем, что намерил. Это занимает несколько минут и требует машину целиком. Число сообщений не фиксировано: пробный прогон подбирает его так, чтобы каждый измеряемый прогон длился не меньше пяти секунд на той машине, где его снимают.

just bench-code

Рецепт поднимает тот же стенд, подсчитывает таблицу кода под valgrind, гасит стенд и переписывает раздел code того же документа. Он занимает меньше минуты, и ему нужны valgrind и исполнитель бенчмарков: cargo install --locked gungraun-runner --version =0.19.4.