
U PHP ekosustavu trenutno postoje dva konektora za rad sa Tarantool serverom ― to je službeni PECL dodatak , napisan u C, i , napisan u PHP. Ja sam autor potonjeg.
U ovom članku bih želio podijeliti rezultate testiranja performansi obiju biblioteka i pokazati kako uz minimalne promjene u kodu može doći do 3-5% povećanja performansi (na sintetičkim testovima!).
Što ćemo testirati?
Testirat ćemo ranije spomenute sinkrone konektore, pokrenute asinkrono, paralelno i asinkrono-paralelno. 🙂 Također ne želimo dirati sam kod konektora. Trenutno je dostupno nekoliko dodataka koji omogućuju postizanje željenog:
- ― visokoučinkovit asinkroni okvir za PHP. Koriste ga internetski divovi kao što su Alibaba i Baidu. Od verzije 4.1.0 uvedena je čarobna metoda SwooleRuntime::enableCoroutine(), koja "jednom linijom koda pretvara sinkrone mrežne biblioteke PHP u asinkrone".
- Async ― do nedavno vrlo perspektivan dodatak za asinkroni rad u PHP-u. Zašto do nedavno? Nažalost, iz meni nepoznatog razloga, autor je uklonio repozitorij i daljnja sudbina projekta je neizvjesna. Morat ćemo se poslužiti od forkova. Kao i Swoole, ovaj dodatak omogućava jednim potezom transformaciju rada u asinkronost zamjenom standardne implementacije TCP i TLS tokova njihovim asinkronim verzijama. To se postiže putem opcije "async.tcp = 1«.
- ― prilično novi dodatak od poznatog Joea Watkina, autora knjižnica kao što su phpdbg, apcu, pthreads, pcov, uopz. Dodatak pruža API za višedretvenog rada u PHP-u i pozicionira se kao zamjena za pthreads. Značajno ograničenje knjižnice je to što radi samo s ZTS (Zend Thread Safe) verzijom PHP-a.
Kako ćemo testirati?
Pokrenut ćemo instancu Tarantoola s isključenim dnevnikom unaprijed zapisa (wal_mode = none) i povećanom mrežnom baferom (readahead = 1 * 1024 * 1024). Prva opcija isključuje rad s diskom, a druga omogućava čitanje više zahtjeva iz bafera operativnog sustava i time minimizira broj sistemskih poziva.
Za benchmarke koji rade s podacima (ubacivanje, brisanje, čitanje itd.) prije početka benchmarka bit će (ponovno) stvoren memtx-prostor u kojem se vrijednosti primarnog indeksa stvaraju generatorom urednih cijelih brojeva (sekvenca).
DDL prostora izgleda ovako:
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}})Po potrebi, prije pokretanja benchmarka, prostor se ispunjava sa 10.000 trokuta u obliku
{id, "tuplе_"}Pristup trokutima se ostvaruje kroz nasumičnu vrijednost ključa.
Sam benchmark predstavlja jedan zahtjev prema serveru, koji se izvršava 10.000 puta (revolucija), koje se, zauzvrat, izvršavaju u iteracijama. Iteracije se ponavljaju sve dok sva odstupanja u vremenu između 5 iteracija ne budu unutar prihvatljive pogreške od 3 %*. Nakon toga se uzima prosječan rezultat. Između iteracija pauza od 1 sekunde, kako se procesor ne bi pregrijao. Lua sakupljač smeća se isključuje prije svake iteracije i prisilno pokreće nakon njenog završetka. PHP proces se pokreće samo s potrebnim ekstenzijama za benchmark, s uključenim međuspremnikom izlaznih podataka i isključenim sakupljačem smeća.
* Broj revolucija, iteracija i prag pogreške može se mijenjati u postavkama benchmarka.
Testno okruženje
Objavljeni rezultati napravljeni su na MacBookPro (2015), operacijski sustav — Fedora 30 (verzija kernela 5.3.8-200.fc30.x86_64). Tarantool je pokrenut u Dockeru sa parametrom "--network host".
Verzije paketa:
Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (izgrađeno: 22. listopada 2019. 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch za 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallel: 1.1.3
* Nažalost, službeni konektor ne radi s verzijom PHP > 7.2. Da bi se kompilirao i pokrenuo ekstenzija na PHP 7.3, bilo je potrebno koristiti .
Rezultati
Sinkroni način
Protokol Tarantoola koristi binarni format za serijalizaciju poruka. U PECL konektoru serijalizacija je duboko skrivena u utrobi biblioteke i utjecati na proces kodiranja iz userland-koda . Konektor na čistom PHP, s druge strane, omogućuje prilagodbu procesa kodiranja proširenjem standardnog kodera ili mogućnošću korištenja vlastite implementacije. Iz kutije su dostupna dva kodera, jedan temeljen na (službeno proširenje MessagePack PECL), drugi — na (na čistom PHP-u).
Prije usporedbe konektora, izmjerit ćemo performanse MessagePack kodera za PHP konektor, a u daljnjim testovima koristit ćemo onaj koji pokaže najbolje rezultate:

Iako PHP verzija (Pure) zaostaje za PECL proširenjem u brzini, u stvarnim projektima i dalje bih preporučio korištenje , jer je u službenom proširenju MessagePack specifikacija formata implementirana samo djelomično (na primjer, nema podrške za korisničke tipove podataka, bez kojih nećete moći koristiti Decimal — novi tip podataka predstavljen u Tarantool 2.3) i ima niz drugih (uključujući probleme s kompatibilnošću s PHP 7.4). I općenito, projekt izgleda napušten.
Dakle, izmjerimo performanse konektora u sinhronom režimu:

Kao što se može vidjeti s grafikona, PECL konektor (Tarantool) pokazuje bolje performanse u usporedbi s konektorom na PHP-u (Client). No, to ne iznenađuje, s obzirom na to da potonji, osim što je implementiran na sporijem jeziku, zapravo obavlja više posla: pri svakom pozivu stvara se novi objekt Request i Response (u slučaju Select ― još i Criteria, a u slučaju Update/Upsert ― Operations), odvojene entitete Connection, Packer i Handler također dodaje dodatno opterećenje. Očito je da za fleksibilnost treba platiti. Međutim, općenito, PHP interpreter pokazuje dobre performanse, iako razlika postoji, ona nije značajna i možda će biti još manja korištenjem preloading-a u PHP 7.4, ne spominjući JIT u PHP 8.
Idemo dalje. U Tarantool 2.0 dodana je podrška za SQL. Pokušajmo izvršiti operacije Select, Insert, Update i Delete koristeći SQL protokol i usporedimo rezultate s noSQL (binarnih) ekvivalentima:

Rezultati SQL-a nisu previše impresivni (podsjećam da još uvijek testiramo sinhroni režim). Međutim, ne bih se previše brinuo zbog toga unaprijed, podrška za SQL je još uvijek u aktivnom razvoju (relativno nedavno, na primjer, dodana je podrška za ) i, sudeći po popisu , SQL engine očekuje niz optimizacija.
Asinkrono
Pa da vidimo kako će proširenje Asinkrono pomoći u poboljšanju gornjih rezultata. Za pisanje asinkronih programa proširenje pruža API temeljen na korutinama, koji ćemo iskoristiti. Eksperimentišemo i utvrđujemo da je optimalan broj korutina za naše okruženje 25:

»Raspoređujemo« 10,000 operacija među 25 korutina i gledamo što smo postigli:

Broj operacija u sekundi povećan je više od 3 puta za !
Nažalost, PECL konektor se nije pokrenuo s ext-async.
A što je s SQL-om?

Kao što vidite, u asinkronom režimu razlika između binarnog protokola i SQL-a postala je unutar greške.
Swoole
Ponovo utvrđujemo optimalan broj korutina, sada za Swoole:

Ostaćemo na 25. Ponovimo istu stvar kao i s proširenjem Asinkrono ― raspodelimo 10,000 operacija među 25 korutina. Osim toga, dodajemo još jedan test, u kojem ćemo podeliti sav rad na 2 procesa (tj. svaki proces će izvršiti 5,000 operacija u 25 korutina). Procesi će se kreirati pomoću SwooleProcess.
Rezultati:

Swole pokazuje malo niži rezultat u poređenju s Asinkronim kada se pokreće u jednom procesu, ali s 2 procesa situacija se drastično menja (broj 2 nije izabran slučajno, na mojoj mašini su baš 2 procesa pokazala najbolje rezultate).
Usput, i u proširenju Asinkrono postoji API za rad s procesima, međutim, nisam primetio razliku u pokretanju benchmarkova u jednom ili više procesa (nije isključeno da sam napravio neku grešku).
SQL vs binarni protokol:

Kao i s Asinkronim, razlika između binarnih i SQL operacija se smanjuje u asinkronom režimu.
Parallel
Budući da proširenje Paralelno nije o korutinama, već o procesima, izmerićemo optimalan broj paralelnih procesa:

On iznosi 16 na mojoj mašini. Pokrenimo benchmarke konektora na 16 paralelnih procesa:

Kao što vidite, rezultat je čak i bolji nego s asinkronim proširenjima (ne računajući Swoole pokrenut na 2 procesa). Imajte na umu da za PECL konektor na mestima Update i Upsert operacija nema podataka. To je zbog toga što su te operacije završile greškom ― ne znam da li je krivnja ext-parallel, ext-tarantool ili obojica.
Sada uporedimo performanse SQL-a:

Primijetili ste sličnost s grafikom za konektore pokrenute sinkrono?
Sve zajedno
Na kraju, sažet ćemo sve rezultate u jedan grafikon kako bismo vidjeli cjelokupnu sliku za testirane ekstenzije. Na grafikon ćemo dodati samo jedan novi test koji do sada nismo radili - pokrenut ćemo Async korutine paralelno s Parallel*. Ideja integracije gore spomenutih ekstenzija već od strane autora, ali konsenzus nije postignut, morat ćemo to učiniti sami.
* Nije uspjelo pokrenuti Swoole korutine s Parallel, čini se da su ove ekstenzije nespojive.
Dakle, konačni rezultati:

Umjesto zaključka
Po mom mišljenju, rezultati su prilično dobri i nekako sam uvjeren da to još nije max! Trebate li to u stvarnom projektu, isključivo je na vama da donesete odluku, samo ću reći da je za mene ovo bio zanimljiv eksperiment koji omogućava procjenu koliko se može "izvući" iz sinkroniziranog TCP konektora uz minimalan trud. Ako imate ideje za poboljšanje benchmarkova - rado ću razmotriti vaš pull request. Sav kod s uputama za pokretanje i rezultatima objavljen je u zasebnom .
Izvor: habr.com
