Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.
Методология
Как ruststream-lapin измеряет себя: три цикла, прогон на настоящем узле, арифметика «упирается в
брокер» и подсчёт кода. Процедура общая для всего фреймворка
(методология RustStream),
а эта страница говорит, как она выглядит на RabbitMQ.
Три цикла
Один процесс прогоняет каждый сценарий трижды, тремя циклами, которые различаются одним: тем, что переносит сообщения.
- Сырой клиент.
lapin, которым напрямую управляет цикл, написанный вручную: получить, декодировать, сделать ack. - Крейт брокера. Этот крейт и ничего над ним: брокер подключается, дескриптор очереди открывает подписку, а цикл забирает доставки из её потока, декодирует их и подтверждает.
- RustStream. Сервис, который пишет пользователь: обработчик
#[subscriber]и приложение вокруг него.
Всё остальное в трёх циклах одинаково: соединение, канал, который получает подписка, аргументы очереди, окно предвыборки, место ack, декодирование в тот же тип, байты тела, рантайм tokio и бинарник.
Тело сообщения - 512 байт JSON, опубликованных без требования сохранности. Классическая очередь держит его в памяти, а у кворумной очереди нет другого режима, кроме собственного реплицированного хранения.
Прогон
- Новая очередь на каждый прогон. Каждый прогон объявляет собственную очередь и удаляет её после себя, так что ни один цикл не разбирает то, что оставил предыдущий прогон.
- Окно открывается на первой доставке и закрывается, когда закончена работа над последней, одинаково во всех трёх циклах. Каждый цикл подтверждает доставку после этой точки, так что одно подтверждение из миллионов в прогоне остаётся за пределами числа в каждом из них.
- Число сообщений задаёт проба: она подбирает его так, чтобы каждый измеряемый прогон длился не меньше пяти секунд на любой машине.
- Циклы чередуются: сырой, крейт, RustStream, три круга. Каждый цикл сообщает свой лучший, медианный и худший круг.
Строка помечается «упирается в брокер», когда сырой клиент провёл прогон в ожидании сервера, а не в
работе. Это арифметика: проба вне кругов замеряет один basic.qos, на который отвечает сервер, и
отметка появляется, когда круговые задержки, которые стоит одна доставка, набирают не меньше
половины времени сообщения. Круговая задержка публикуется вместе с машиной, так что арифметику можно
проверить. Доставка в AMQP не ждёт собственного оборота: сервер сам присылает тело и не отвечает на
подтверждение. Ждать приходится кредита, который возвращает подтверждение, а окно предвыборки
растягивает это ожидание на все сообщения окна.
Подсчёт кода
Каждый сценарий графика стоимости - сервис, который пишет
пользователь, на LapinBroker, подключённом к узлу из стенда docker compose, собранный без фичи
testing. Сервис объявляет классическую очередь при запуске, читает её с окном предвыборки из
сравнения и работает на однопоточном рантайме tokio. Очередь наполняется из другого потока до того,
как сервис возьмётся за первое сообщение, и наполнение ждёт, пока узел подтвердит каждое сообщение.
Инструкции считает callgrind, выделения памяти - DHAT. Сценарий прогоняется на одной доставке, затем на 1000, затем на 2000. Разность двух последних - установившаяся цена сообщения, а одна доставка - холодный старт.
Настоящий узел отвечает в своём темпе, поэтому то, как часто поток сервиса ждёт сокет, от прогона к
прогону меняется. За семь прогонов одного и того же кода инструкции расходились до 2,4 процента, а
выделения памяти - не больше чем на один блок. Порог выделений в сценарии стоит на десятую долю
процента выше самого большого числа, которое он давал, и just bench-code завершается с ошибкой, если выделений
больше. С --baseline=main рецепт завершается с ошибкой ещё и при приросте инструкций больше пяти процентов;
простой прогон инструкции не ограничивает. Прогон, завершившийся с ошибкой, всё равно печатает таблицу, а под ней
каждый нарушенный предел: старое значение рядом с новым.
Как повторить
just bench
Рецепт поднимает узел из docker-compose.test.yml, прогоняет оба сценария сравнения, гасит узел и
переписывает docs/benchmarks/results.json. Это занимает несколько минут и требует машину целиком.
Рецепт очищает RUSTFLAGS, поэтому бинарник не привязан к процессору, на котором его собрали.
Оба рецепта собирают замеры так же, как собирается выпуск: с полной оптимизацией при компоновке
через границы крейтов. Поэтому числа описывают тот код, который получает сервис.
just bench-code
Подсчёт кода занимает меньше минуты; нужен valgrind. Исполнитель бенчмарков рецепт ставит сам, в
той версии, которую закрепляет Cargo.lock. Число после имени рецепта задаёт количество доставок в
измеряемом прогоне, например just bench-code 2000; опубликованные числа сняты при значении по
умолчанию, 1000. just bench-code --save-baseline=main на main записывает базовую линию, а
just bench-code --baseline=main на ветке сравнивает с ней.