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

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

Методология

Как 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 на ветке сравнивает с ней.