- PVSM.RU - https://www.pvsm.ru -
В экосистеме PHP на данный момент существует два коннектора для работы с сервером Tarantool ― это официальное расширение PECL tarantool/tarantool-php [1], написанное на С, и tarantool-php/client [2], написанный на PHP. Я являюсь автором последнего.
В этой статье я хотел бы поделиться результатами тестирования производительности обеих библиотек и показать, как с помощью минимальных изменений в коде можно добиться 3-5 прироста производительности (на синтетический тестах!).
Будем тестировать упомянутые выше синхронные коннекторы, запущенные асинхронно, параллельно и асинхронно-параллельно. :) Также мы не хотим трогать код самих коннекторов. На данный момент доступно несколько расширений, позволяющих добиться желаемого:
Запустим экземпляр Tarantool'а с отключенным журналом упреждающей записи (wal_mode = none) и увеличенным сетевым буфером (readahead = 1 * 1024 * 1024). Первая опция исключит работу с диском, вторая — даст возможность вычитывать больше запросов из буфера операционной системы и тем самым минимизировать количество системных вызовов.
Для бенчмарков, которые работают с данными (вставка, удаление, чтение и т.д.) перед стартом бенчмарка будет (пере)создаваться memtx-спейс, в котором значения первичного индекса создаются генератором упорядоченных значений целых чисел (sequence).
DDL спейса выглядит так:
space = box.schema.space.create(config.space_name, {id = config.space_id, temporary = true})
space:create_index('primary', {type = 'tree', parts = {1, 'unsigned'}, sequence = true})
space:format({{name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false}})
По необходимости, перед запуском бенчмарка, спейс заполняется 10,000 кортежами вида
{id, "tuplе_<id>"}
Доступ к кортежам осуществляется по рандомному значению ключа.
Сам бенчмарк представляет собой единичный запрос к серверу, который выполняется 10,000 раз (революций), которые, в свою очередь, выполняются в итерациях. Итерации повторяются до тех пор, пока все отклонения во времени между 5 итерациями не окажутся в пределах допустимой погрешности в 3 %*. После этого берется усредненный результат. Между итерациями пауза в 1 секунду, чтобы не дать процессору уйти в throttling. Сборщик мусора Lua отключается перед каждой итерацией и принудительно запускается после ее завершения. PHP-процесс запускается только с необходимыми для бенчмарка расширениями, с включенной буферизацией вывода и выключенным сборщиком мусора.
* Количество революций, итераций и порог погрешности можно изменить в настройках бенчмарка.
Опубликованные ниже результаты были сделаны на MacBookPro (2015), операционная система — Fedora 30 (версия ядра 5.3.8-200.fc30.x86_64). Tarantool запускался в докере в с параметром "--network host"
.
Версии пакетов:
Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (built: Oct 22 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ патч для 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallel: 1.1.3
* К сожалению, официальный коннектор не работает с версией PHP > 7.2. Чтобы скомпилировать и запустить расширение на PHP 7.3, пришлось воспользоваться патчем [6].
Протокол Tarantool’а использует бинарный формат MessagePack [7] для сериализации сообщений. В коннекторе PECL сериализация скрыта глубоко в недрах библиотеки и повлиять на процесс кодирования из userland-кода не представляется возможным [8]. Коннектор на чистом PHP, напротив, предоставляет возможность кастомизации процесса кодирования расширением стандартного кодировщика либо возможностью использования своей реализации. Из коробки доступно два кодировщика, один основан на msgpack/msgpack-php [9] (официальное расширение MessagePack PECL), другой — на rybakit/msgpack [10] (на чистом PHP).
Перед сравнением коннекторов, измерим производительность MessagePack кодировщиков для PHP-коннектора и в дальнейших тестах будем использовать тот, который покажет лучший результат:
Хоть PHP версия (Pure) и уступает расширению PECL в скорости, в реальных проектах я бы все же рекомендовал использовать именно rybakit/msgpack [10], потому как в официальном расширении MessagePack спецификация формата реализована лишь частично (например, нет поддержки пользовательских типов данных, без которой вы не сможете использовать Decimal — новый тип данных, представленный в Tarantool 2.3) и имеет ряд других проблем [11] (включая проблемы совместимости с PHP 7.4). Ну и в целом, проект выглядит заброшенным.
Итак, измерим производительность коннекторов в синхронном режиме:
Как видно из графика, коннектор PECL (Tarantool) показывает лучшую производительность по сравнению с коннектором на PHP (Client). Но это и не удивительно, учитывая, что последний, помимо того, что реализован на более медленном языке, выполняет, по сути, больше работы: при каждом вызове создается новый объект Request и Response (в случае Select ― еще и Criteria, а в случае Update/Upsert ― Operations), отдельные сущности Connection, Packer и Handler тоже добавляют оверхед. Очевидно, что за гибкость приходится платить. Однако, в целом, PHP-интерпретатор показывает хорошую производительность, хоть разница и есть, но она незначительна и, возможно, будет еще меньше при использовании preloading в PHP 7.4, не говоря уже о JIT в PHP 8.
Двигаемся дальше. В Tarantool 2.0 появилась поддержка SQL. Попробуем выполнить операции Select, Insert, Update и Delete используя SQL-протокол и сравнить результаты с noSQL (бинарными) эквивалентами:
Результаты SQL не сильно впечатляют (напомню, что мы все еще тестируем синхронный режим). Однако, я бы не стал расстраиваться по этому поводу раньше времени, поддержка SQL все еще находится в активной разработке (относительно недавно, например, добавилась поддержка prepared statements [12]) и, судя по списку issues [13], движок SQL в дальнейшем ждет ряд оптимизаций.
Ну что ж, посмотрим теперь, как расширение Async сможет помочь нам улучшить результаты выше. Для написания асинхронных программ расширение предоставляет API на основе корутин (coroutines), им и воспользуемся. Опытным путем выясняем, что оптимальное количество корутин для нашего окружения равно 25:
«Размазываем» 10,000 операций по 25 корутинам и смотрим, что получилось:
Количество операций в секунду выросло более чем в 3 раза для tarantool-php/client [2]!
Печально, но коннектор PECL не запустился с ext-async.
А что c SQL?
Как видите, в асинхронном режиме разница между бинарным протоколом и SQL стала в пределах погрешности.
Опять выясняем оптимальное количество корутин, теперь уже для Swoole:
Остановимся на 25. Повторим тот же трюк, что и с расширением Async ― распределим 10,000 операций между 25 корутинами. Помимо этого, добавим еще тест, в котором разделим всю работу на 2 два процесса (то есть каждый процесс будет выполнять 5,000 операций в 25 корутинах). Процессы будут создаваться при помощи SwooleProcess.
Результаты:
Swole показывает чуть более низкий результат по сравнению с Async при запуске в одном процессе, но с 2 процессами картина меняется кардинально (число 2 выбрано не случайно, на моей машине именно 2 процесса показали наилучший результат).
Кстати, в расширении Async тоже есть API для работы с процессами, однако там я не заметил какой-то разницы от запуска бенчмарков в одном или нескольких процессах (не исключено, что я где-то накосячил).
SQL vs бинарный протокол:
Так же как и с Async, разница между бинарными и SQL-операциями нивелируется в асинхронном режиме.
Так как расширение Parallel не про корутины, но про потоки, измерим оптимальное количество параллельных потоков:
Оно равно 16 на моей машине. Запустим бенчмарки коннекторов на 16 параллельных потоках:
Как видите, результат даже лучше, чем с асинхронными расширениями (не считая Swoole запущенном на 2 процессах). Заметьте, что для коннектора PECL на месте Update и Upsert операций пусто. Связано это с тем, что данные операции вылетели с ошибкой ― не знаю, по вине ext-parallel, ext-tarantool или обоих.
Теперь сравним производительность SQL:
Заметили сходство с графиком для коннекторов, запущенных синхронно?
Ну и напоследок, сведем все результаты в один график, чтобы увидеть общую картину для тестируемых расширений. Добавим на график лишь один новый тест, который мы еще не делали ― запустим корутины Async параллельно при помощи Parallel*. Идея интеграции вышеупомянутых расширений уже обсуждалась [14] авторами, однако консенсус так и не был достигнут, придется делать это самим.
* Запустить корутины Swoole с Parallel не получилось, похоже эти расширения несовместимы.
Итак, финальные результаты:
По-моему, результаты получились весьма достойные, и я почему-то уверен, что это еще не предел! Нужно ли это вам в реальном проекте решать исключительно вам самим, скажу лишь, что для меня это был интересный эксперимент, позволяющий оценить, сколько можно «выжать» из синхронного TCP-коннектора с минимальными усилиями. Если у вас есть идеи по улучшению бенчмарков ― я с радостью рассмотрю ваш пул реквест. Весь код с инструкциями по запуску и результатами опубликован в отдельном репозитории [15].
Автор: gen
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/338567
Ссылки в тексте:
[1] tarantool/tarantool-php: https://github.com/tarantool/tarantool-php
[2] tarantool-php/client: https://github.com/tarantool-php/client
[3] Swoole: https://github.com/swoole/swoole-src
[4] одним: https://github.com/dreamsxin/ext-async
[5] Parallel: https://github.com/krakjoe/parallel
[6] патчем: https://github.com/tarantool/tarantool-php/pull/148/files
[7] MessagePack: https://msgpack.org/
[8] не представляется возможным: https://github.com/tarantool/tarantool-php/issues/89
[9] msgpack/msgpack-php: https://github.com/msgpack/msgpack-php/
[10] rybakit/msgpack: https://github.com/rybakit/msgpack.php
[11] проблем: https://github.com/msgpack/msgpack-php/issues
[12] prepared statements: https://github.com/tarantool/tarantool/commit/ff2091e09b5a7e9b7aa3f3e996dc7a06189889f3
[13] issues: https://github.com/tarantool/tarantool/issues?q=is%3Aissue+is%3Aopen+sql+label%3Aperformance
[14] обсуждалась: https://github.com/krakjoe/parallel/issues/25
[15] репозитории: https://github.com/tarantool-php/benchmarks
[16] Источник: https://habr.com/ru/post/478336/?utm_campaign=478336&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.