Бенчмарки¶
Фреймворк между клиентом 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. Так диск под сервером не попадает в измерение, которое посвящено диспетчеризации. Стрим на файловом хранилище отвечает на другой вопрос и отвечает про сервер, а не про этот крейт.
Числа - снимок одной машины в один день. Их переснимают вручную на машине, отданной одному этому замеру: разница, о которой говорит эта страница, меньше шума машины, которую делят с другими задачами.
Как повторить¶
Рецепт поднимает стенд из docker-compose.test.yml, прогоняет оба сценария, гасит стенд и
переписывает docs/benchmarks/results.json тем, что намерил. Это занимает несколько минут и
требует машину целиком. Число сообщений не фиксировано: пробный прогон подбирает его так, чтобы
каждый измеряемый прогон длился не меньше пяти секунд на той машине, где его снимают.
Рецепт поднимает тот же стенд, подсчитывает таблицу кода под valgrind, гасит стенд и переписывает
раздел code того же документа. Он занимает меньше минуты, и ему нужны valgrind и исполнитель
бенчмарков: cargo install --locked gungraun-runner --version =0.19.4.