Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel

У экасістэме PHP на дадзены момант існуе два канектары для працы з серверам Tarantool ― гэта афіцыйнае пашырэнне PECL tarantool/tarantool-php, напісанае на С, і tarantool-php/client, напісаны на PHP. Я з'яўляюся аўтарам апошняга.

У гэтым артыкуле я хацеў бы падзяліцца вынікамі тэсціравання прадукцыйнасці абедзвюх бібліятэк і паказаць, як з дапамогай мінімальных змяненняў у кодзе можна дабіцца 3-5 прыросту прадукцыйнасці.на сінтэтычны тэстах!).

Што будзем тэсціраваць?

Будзем тэсціраваць згаданыя вышэй сінхронныя канектары, запушчаныя асінхронна, паралельна і асінхронна-паралельна. 🙂 Таксама мы не хочам чапаць код саміх канектараў. На дадзены момант даступна некалькі пашырэнняў, якія дазваляюць дамагчыся жаданага:

  • Swoole ― высокапрадукцыйны асінхронны фрэймворк для PHP. Выкарыстоўваецца такімі інтэрнэт-гігантамі як Alibaba і Baidu. З версіі 4.1.0 з'явіўся чароўны метад SwooleRuntime::enableCoroutine(), які дазваляе "адным радком кода пераўтварыць сінхронныя сеткавыя бібліятэкі PHP у асінхронныя".
  • Async ― да нядаўняга часу вельмі перспектыўнае пашырэнне для асінхроннай працы ў PHP. Чаму да нядаўняга? На жаль, па невядомай мне прычыне, аўтар выдаліў рэпазітар і далейшы лёс праекта туманны. Прыйдзецца скарыстацца адным з форкаў. Як і Swoole, гэтае пашырэнне дазваляе лёгкім рухам рукі ператварыць штаны ўключыць асінхроннасць заменай стандартнай рэалізацыі TCP і TLS струменяў іх асінхроннымі версіямі. Робіцца гэта праз опцыюasync.tcp = 1«.
  • Паралельныя ― даволі новае пашырэнне ад даволі вядомага Joe Watkins, аўтара такіх бібліятэк як phpdbg, apcu, pthreads, pcov, uopz. Пашырэнне дае API для шматструменнай працы ў PHP і пазіцыянуецца як замена pthreads. Істотным абмежаваннем бібліятэкі з'яўляецца тое, што яна працуе толькі з ZTS (Zend Thread Safe) версіяй PHP.

Як будзем тэсціраваць?

Запусцім асобнік 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, прыйшлося скарыстацца патчам.

Вынікі

Сінхронны рэжым

Пратакол Tarantool'а выкарыстоўвае бінарны фармат MessagePack для серыялізацыі паведамленняў. У канектары PECL серыялізацыя ўтоена глыбока ў нетрах бібліятэкі і паўплываць на працэс кадавання з userland-кода не ўяўляецца магчымым. Канектар на чыстым PHP, наадварот, дае магчымасць кастамізацыі працэсу кадавання пашырэннем стандартнага кадавальніка альбо магчымасцю выкарыстання сваёй рэалізацыі. Са скрынкі даступна два кадавальнікі, адзін заснаваны на msgpack/msgpack-php (афіцыйнае пашырэнне MessagePack PECL), іншы – на rybakit/msgpack (на чыстым PHP).

Перад параўнаннем канектараў, вымераем прадукцыйнасць MessagePack кадавальнікаў для PHP-канектару і ў далейшых тэстах будзем выкарыстоўваць той, які пакажа лепшы вынік:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Хоць PHP версія (Pure) і саступае пашырэнню PECL у хуткасці, у рэальных праектах я бы ўсё ж рэкамендаваў выкарыстоўваць менавіта rybakit/msgpack, таму як у афіцыйным пашырэнні MessagePack спецыфікацыя фармату рэалізаваная толькі часткова (напрыклад, няма падтрымкі карыстацкіх тыпаў дадзеных, без якой вы не зможаце выкарыстоўваць Decimal - новы тып дадзеных, прадстаўлены ў Tarantool 2.3) і мае шэраг іншых праблем (уключаючы праблемы сумяшчальнасці з PHP 7.4). Ну і ў цэлым, праект выглядае закінутым.

Такім чынам, вымераем прадукцыйнасць канектараў у сінхронным рэжыме:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Як відаць з графіка, канектар PECL (Tarantool) паказвае лепшую прадукцыйнасць у параўнанні з канектарам на PHP (Client). Але гэта і не дзіўна, улічваючы, што апошні, апроч таго, што рэалізаваны на больш павольнай мове, выконвае, па сутнасці, больш працы: пры кожным выкліку ствараецца новы аб'ект Запыт и Адказ (у выпадку Select ― яшчэ і Крытэрыі, а ў выпадку Update/Upsert ― аперацыі), асобныя сутнасці Сувязі, Ўпакоўшчык и Handler таксама дадаюць оверхед. Відавочна, што за гнуткасць даводзіцца плаціць. Аднак, у цэлым, PHP-інтэрпрэтатар паказвае добрую прадукцыйнасць, хоць розніца і ёсць, але яна малаважная і, магчыма, будзе яшчэ менш пры выкарыстанні preloading у PHP 7.4, не кажучы ўжо пра JIT у PHP 8.

Рухаемся далей. У Tarantool 2.0 з'явілася падтрымка SQL. Паспрабуем выканаць аперацыі Select, Insert, Update і Delete выкарыстоўваючы SQL-пратакол і параўнаць вынікі з noSQL (бінарнымі) эквівалентамі:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Вынікі SQL не моцна ўражваюць (нагадаю, што мы ўсё яшчэ тэстуем сінхронны рэжым). Аднак, я б не стаў хвалявацца з гэтай нагоды раней часу, падтрымка SQL усё яшчэ знаходзіцца ў актыўнай распрацоўцы (адносна нядаўна, напрыклад, дадалася падтрымка падрыхтаваныя заявы) і, мяркуючы па спісе пытанняў, рухавічок SQL у далейшым чакае шэраг аптымізацый.

Async

Ну што ж, паглядзім зараз, як пашырэнне Async зможа дапамагчы нам палепшыць вынікі вышэй. Для напісання асінхронных праграм пашырэнне падае API на аснове каруцін (coroutines), ім і скарыстаемся. Доследным шляхам высвятляем, што аптымальная колькасць каруцін для нашага атачэння роўна 25:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
«Размазваем» 10,000 аперацый па 25 каруцінах і глядзім, што атрымалася:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Колькасць аперацый у секунду вырасла больш за ў 3 разу для tarantool-php/client!

Сумна, але канектар PECL не запусціўся з ext-async.

А што з SQL?

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Як бачыце, у асінхронным рэжыме розніца паміж бінарным пратаколам і SQL стала ў межах хібнасці.

Swoole

Ізноў высвятляем аптымальную колькасць каруцін, зараз ужо для Swoole:
Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Спынімся на 25. Паўторым той жа трук, што і з пашырэннем Async ― размяркуем 10,000 аперацый паміж 25 каруцінамі. Акрамя гэтага, дадамо яшчэ тэст, у якім падзелім усю працу на 2 два працэсы (гэта значыць кожны працэс будзе выконваць 5,000 аперацый у 25 каруцінах). Працэсы будуць стварацца пры дапамозе SwooleProcess.

вынікі:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Swole паказвае крыху ніжэйшы вынік у параўнанні з Async пры запуску ў адным працэсе, але з 2 працэсамі карціна змяняецца кардынальна (лік 2 абрана не выпадкова, на маёй машыне менавіта 2 працэсу паказалі найлепшы вынік).

Дарэчы, у пашырэнні Async таксама ёсць API для працы з працэсамі, аднак тамака я не заўважыў нейкай розніцы ад запуску бенчмаркаў у адным або некалькіх працэсах (не выключана, што я дзесьці накасячыў).

SQL vs бінарны пратакол:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Гэтак жа як і з Async, розніца паміж бінарнымі і SQL-аперацыямі нівеліруецца ў асінхронным рэжыме.

Паралельныя

Бо пашырэнне Parallel не пра каруціны, але пра струмені, вымераем аптымальную колькасць раўналежных струменяў:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Яно роўна 16 на маёй машыне. Запусцім бенчмаркі канектараў на 16 паралельных патоках:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Як бачыце, вынік нават лепш, чым з асінхроннымі пашырэннямі (акрамя Swoole запушчаным на 2 працэсах). Заўважце, што для канектара PECL на месцы Update і Upsert аперацый пуста. Звязана гэта з тым, што дадзеныя аперацыі вылецелі з памылкай - не ведаю, па віне ext-parallel, ext-tarantool або абодвух.

Цяпер параўнаем прадукцыйнасць SQL:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel
Заўважылі падабенства з графікам для канектараў, запушчаных сінхронна?

Ўсе разам

Ну і напрыканцы, звядзём усе вынікі ў адзін графік, каб убачыць агульную карціну для тэстоўваных пашырэнняў. Дадамо на графік толькі адзін новы тэст, які мы яшчэ не рабілі ― запусцім каруціны Async паралельна пры дапамозе Parallel*. Ідэя інтэграцыі вышэйзгаданых пашырэнняў ужо абмяркоўвалася аўтарамі, аднак кансэнсус так і не быў дасягнуты, давядзецца рабіць гэта самім.

* Запусціць каруціны Swoole з Parallel не атрымалася, падобна гэтыя пашырэнні несумяшчальныя.

Такім чынам, фінальныя вынікі:

Паскараем PHP-канектары для Tarantool з дапамогай Async, Swoole і Parallel

замест заключэння

Па-мойму, вынікі атрымаліся вельмі вартыя, і я чамусьці ўпэўнены, што гэта яшчэ не мяжа! Ці трэба гэта вам у рэальным праекце вырашаць выключна вам самім, скажу толькі, што для мяне гэта быў цікавы эксперымент, які дазваляе ацаніць, колькі можна "выціснуць" з сінхроннага TCP-канектара з мінімальнымі намаганнямі. Калі ў вас ёсць ідэі па паляпшэнні бенчмаркаў ― я з радасцю разгледжу ваш пул рэквест. Увесь код з інструкцыямі па запуску і вынікамі апублікаваны ў асобным рэпазітары.

Крыніца: habr.com

Дадаць каментар