Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel

În ecosistemul PHP există în prezent doi conectori pentru lucrul cu serverul Tarantool - aceasta este extensia oficială PECL tarantool/tarantool-php, scris în C și tarantool-php/client, scris în PHP. Eu sunt autorul celui din urmă.

În acest articol, aș dori să împărtășesc rezultatele testării performanței ambelor biblioteci și să arăt cum, cu modificări minime ale codului, puteți obține o creștere a performanței cu 3-5 (la teste sintetice!).

Ce vom testa?

Le vom testa pe cele menționate mai sus sincron conectori care rulează asincron, în paralel și asincron-paralel. 🙂 De asemenea, nu vrem să atingem codul conectorilor înșiși. În prezent, există mai multe extensii disponibile pentru a obține ceea ce doriți:

  • Swoole ― un cadru asincron de înaltă performanță pentru PHP. Folosit de giganți ai internetului precum Alibaba și Baidu. Din versiunea 4.1.0 a apărut o metodă magică SwooleRuntime::enableCoroutine(), care vă permite să „conversia biblioteci de rețea PHP sincrone în cele asincrone cu o singură linie de cod”.
  • Async a fost până de curând o extensie foarte promițătoare pentru lucrul asincron în PHP. De ce până de curând? Din păcate, dintr-un motiv necunoscut pentru mine, autorul a șters depozitul și soarta viitoare a proiectului este neclară. Va trebui să-l folosesc unul de la furci. La fel ca Swoole, această extensie vă permite să vă porniți cu ușurință pantalonii cu o mișcare a încheieturii mâinii pentru a activa asincronia prin înlocuirea implementării standard a fluxurilor TCP și TLS cu versiunile lor asincrone. Acest lucru se face prin opțiunea „async.tcp = 1“.
  • Paralel ― o extensie destul de nouă de la binecunoscutul Joe Watkins, autor al unor biblioteci precum phpdbg, apcu, pthreads, pcov, uopz. Extensia oferă un API pentru multithreading în PHP și este poziționată ca înlocuitor pentru pthreads. O limitare semnificativă a bibliotecii este că funcționează numai cu versiunea PHP ZTS (Zend Thread Safe).

Cum vom testa?

Haideți să lansăm o instanță Tarantool cu ​​înregistrarea înainte de scriere dezactivată (wal_mode = niciunul) și tampon de rețea crescut (readhead = 1 * 1024 * 1024). Prima opțiune va elimina lucrul cu discul, a doua va face posibilă citirea mai multor solicitări din buffer-ul sistemului de operare și, prin urmare, va minimiza numărul de apeluri de sistem.

Pentru benchmark-urile care lucrează cu date (inserare, ștergere, citire etc.), înainte de a începe benchmark-ul, va fi (re)creat un spațiu memtx, în care valorile indexului primar sunt create de un generator de valori întregi ordonate ​(secventa).
Spațiul DDL arată astfel:

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}})

Dacă este necesar, înainte de rularea benchmark-ului, spațiul este umplut cu 10,000 de tupluri ale formularului

{id, "tuplе_<id>"}

Tuplurile sunt accesate folosind o valoare cheie aleatorie.

Benchmark-ul în sine este o singură cerere către server, care este executată de 10,000 de ori (revoluții), care, la rândul lor, sunt executate în iterații. Iterațiile se repetă până când toate abaterile de timp între 5 iterații sunt în limitele unei erori acceptabile de 3%*. După aceasta, se ia rezultatul mediu. Există o pauză de 1 secundă între iterații pentru a preveni accelerarea procesorului. Colectorul de gunoi al lui Lua este dezactivat înainte de fiecare iterație și este forțat să pornească după ce se încheie. Procesul PHP este lansat doar cu extensiile necesare pentru benchmark, cu bufferingul de ieșire activat și colectorul de gunoi dezactivat.

* Numărul de rotații, iterații și pragul de eroare pot fi modificate în setările de referință.

Mediu de testare

Rezultatele publicate mai jos au fost realizate pe un MacBookPro (2015), sistem de operare - Fedora 30 (versiunea de kernel 5.3.8-200.fc30.x86_64). Tarantool a fost lansat în docker cu parametrul "--network host".

Versiuni de pachet:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, construiți a872fc2f86
PHP: 7.3.11 (cli) (construit: 22 octombrie 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ plasture pentru 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-woole: 4.4.12
ext-paralel: 1.1.3

* Din păcate, conectorul oficial nu funcționează cu versiunea PHP > 7.2. Pentru a compila și a rula extensia pe PHP 7.3, a trebuit să folosesc plasture.

Constatări

Modul sincron

Protocolul Tarantool folosește un format binar MessagePack pentru a serializa mesajele. În conectorul PECL, serializarea este ascunsă adânc în profunzimea bibliotecii și afectează procesul de codificare din codul userland nu pare posibil. Un conector PHP pur, dimpotrivă, oferă posibilitatea de a personaliza procesul de codificare prin extinderea codificatorului standard sau prin utilizarea propriei implementări. Există două codificatoare disponibile din cutie, pe care se bazează unul msgpack/msgpack-php (extensia oficială MessagePack PECL), cealaltă este activată rybakit/msgpack (în PHP pur).

Înainte de a compara conectorii, vom măsura performanța codificatoarelor MessagePack pentru conectorul PHP și în teste ulterioare îl vom folosi pe cel care arată cel mai bun rezultat:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Deși versiunea PHP (Pure) este inferioară extensiei PECL ca viteză, în proiecte reale aș recomanda totuși utilizarea acesteia rybakit/msgpack, deoarece în extensia oficială MessagePack specificația formatului este implementată doar parțial (de exemplu, nu există suport pentru tipuri de date personalizate, fără de care nu veți putea folosi Decimal - un nou tip de date introdus în Tarantool 2.3) și are un număr de altele Probleme (inclusiv probleme de compatibilitate cu PHP 7.4). Ei bine, în general, proiectul pare abandonat.

Deci, să măsurăm performanța conectorilor în modul sincron:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
După cum se poate observa din grafic, conectorul PECL (Tarantool) prezintă performanțe mai bune în comparație cu conectorul PHP (Client). Dar acest lucru nu este surprinzător, având în vedere că acesta din urmă, pe lângă faptul că este implementat într-un limbaj mai lent, de fapt lucrează mai mult: un nou obiect este creat la fiecare apel. Cerere и Răspuns (în cazul Select - de asemenea Criterii, iar în cazul Update/Upsert ― Operațiuni), entități separate Conexiune, Ambalator и Manipulant ele adaugă și peste cap. Evident, flexibilitatea are un preț. Totuși, în general, interpretul PHP arată performanțe bune, deși există o diferență, este nesemnificativ și, poate, va fi și mai puțin atunci când se folosește preîncărcarea în PHP 7.4, ca să nu mai vorbim de JIT în PHP 8.

Sa trecem peste. Tarantool 2.0 a introdus suport SQL. Să încercăm să efectuăm operațiuni de Selectare, Inserare, Actualizare și Ștergere folosind protocolul SQL și să comparăm rezultatele cu echivalentele noSQL (binare):

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Rezultatele SQL nu sunt foarte impresionante (să vă reamintesc că încă testăm modul sincron). Cu toate acestea, nu m-aș supăra din cauza asta înainte de timp; suportul SQL este încă în curs de dezvoltare activă (relativ recent, de exemplu, a fost adăugat suport declarații pregătite) și, judecând după listă probleme de, motorul SQL va suferi o serie de optimizări în viitor.

async

Ei bine, acum să vedem cum extensia Async ne poate ajuta să îmbunătățim rezultatele de mai sus. Pentru a scrie programe asincrone, extensia oferă un API bazat pe coroutine, pe care îl vom folosi. Aflăm empiric că numărul optim de corutine pentru mediul nostru este 25:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
„Răspândiți” 10,000 de operațiuni în 25 de programe și vedeți ce se întâmplă:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Numărul de operații pe secundă a crescut de peste 3 ori pentru tarantool-php/client!

Din păcate, conectorul PECL nu a început cu ext-async.

Ce zici de SQL?

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
După cum puteți vedea, în modul asincron diferența dintre protocolul binar și SQL a ajuns în marja de eroare.

Swoole

Din nou aflăm numărul optim de corutine, de data aceasta pentru Swoole:
Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Să ne oprim la 25. Să repetăm ​​același truc ca și cu extensia Async - distribuiți 10,000 de operații între 25 de coroutine. În plus, vom adăuga un alt test în care vom împărți toată munca în 2 două procese (adică fiecare proces va efectua 5,000 de operații în 25 de corutine). Procesele vor fi create folosind SwooleProcess.

Rezultate:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Swole arată un rezultat puțin mai scăzut în comparație cu Async atunci când rulează într-un singur proces, dar cu 2 procese imaginea se schimbă dramatic (numărul 2 nu a fost ales întâmplător; pe mașina mea, au fost 2 procese care au arătat cel mai bun rezultat).

Apropo, extensia Async are și un API pentru lucrul cu procese, dar acolo nu am observat nicio diferență față de rularea benchmark-urilor în unul sau mai multe procese (e posibil să fi dat peste cap pe undeva).

SQL vs protocol binar:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Ca și în cazul Async, diferența dintre operațiile binare și SQL este eliminată în modul asincron.

Paralel

Deoarece extensia Parallel nu este despre corutine, ci despre fire, să măsurăm numărul optim de fire paralele:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Este egal cu 16 pe aparatul meu. Să rulăm benchmark-uri pentru conectori pe 16 fire paralele:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
După cum puteți vedea, rezultatul este chiar mai bun decât în ​​cazul extensiilor asincrone (fără a lua în calcul Swoole care rulează pe 2 procese). Rețineți că pentru conectorul PECL, operațiunile Actualizare și Upsert sunt goale. Acest lucru se datorează faptului că aceste operațiuni au eșuat cu o eroare - nu știu dacă a fost vina ext-parallel, ext-tarantool sau ambele.

Acum să comparăm performanța SQL:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel
Observați asemănarea cu graficul pentru conectorii care rulează sincron?

Împreună

Și, în sfârșit, să rezumăm toate rezultatele într-un singur grafic pentru a vedea imaginea de ansamblu pentru extensiile testate. Să adăugăm un singur test nou la diagramă, pe care nu l-am făcut încă - să rulăm coroutine Async în paralel folosind Parallel*. Ideea de a integra extensiile de mai sus este deja discutat autori, dar nu s-a ajuns la un consens, va trebui să o faci singur.

* Nu a fost posibilă lansarea coroutinelor Swoole cu Parallel; se pare că aceste extensii sunt incompatibile.

Deci, rezultatele finale:

Accelerarea conectorilor PHP pentru Tarantool folosind Async, Swoole și Parallel

În loc de concluzie

După părerea mea, rezultatele s-au dovedit a fi destul de demne și, din anumite motive, sunt sigur că aceasta nu este limita! Fie că trebuie să decideți acest lucru într-un proiect real doar pentru dvs., voi spune doar că pentru mine a fost un experiment interesant care vă permite să evaluați cât de mult puteți „strânge” dintr-un conector TCP sincron cu efort minim. Dacă aveți idei pentru îmbunătățirea benchmark-urilor, voi lua în considerare solicitarea dvs. Tot codul cu instrucțiunile de lansare și rezultatele sunt publicate separat depozite.

Sursa: www.habr.com

Adauga un comentariu