Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel

V PHP ekosistemu trenutno postoje dva konektora za rad sa Tarantool serverom - to je službena PECL ekstenzija tarantool/tarantool-php, napisana na C, i tarantool-php/client, napisana na PHP. Ja sam autor ove druge.

U ovom članku želim podeliti rezultate testiranja performansi oba biblioteke i pokazati kako uz minimalne izmene u kodu možemo postići povećanje performansi od 3-5% (na sintetičkim testovima!).

Šta ćemo testirati?

Testiraćemo gore pomenute sinkrone konektore pokrenute asinhrono, paralelno i asinhrono-paralelno. 🙂 Takođe ne želimo dirati kod samih konektora. Trenutno je dostupno nekoliko ekstenzija koje omogućavaju postizanje željenog:

  • Swoole ― visokoperformantni asinhroni okvir za PHP. Koriste ga internet divovi kao što su Alibaba i Baidu. Od verzije 4.1.0 uvedena je čarobna metoda SwooleRuntime::enableCoroutine(), koja omogućava „jednom linijom koda da pretvori sinkrone mrežne biblioteke PHP u asinhrone”.
  • Async ― do nedavno veoma perspektivna ekstenzija za asinhroni rad u PHP. Zašto do nedavno? Nažalost, iz meni nepoznatih razloga, autor je obrisao repozitorijum, a dalja sudbina projekta je nejasna. Moraćemo da koristimo jedan od forka. Kao i Swoole, ova ekstenzija omogućava da se bez puno truda uključi asinhronost zamenom standardnih implementacija TCP i TLS tokova njihovim asinhronim verzijama. To se postiže putem opcije „async.tcp = 1«.
  • Parallel ― prilično nova ekstenzija od poznatog Joe Watkins-a, autora biblioteka kao što su phpdbg, apcu, pthreads, pcov, uopz. Ekstenzija pruža API za multi-thread rad u PHP i pozicionira se kao zamena za pthreads. značajno ograničenje biblioteke je to što radi samo sa ZTS (Zend Thread Safe) verzijom PHP.

Kako ćemo testirati?

Pokrenućemo primerak Tarantoola sa isključenim dnevnikom predhodnog zapisa (wal_mode = none) i povećanim mrežnim baferom (readahead = 1 * 1024 * 1024). Prva opcija isključuje rad sa diskom, dok druga omogućava da se više zahteva čita iz bafera operativnog sistema i time minimizuje broj sistemskih poziva.

Za benchmarke, ki delujejo z podatki (vstavljanje, brisanje, branje itd.), se pred začetkom benchmarka (pre)ustvari memtx-prostor, v katerem vrednosti primarnega indeksa ustvarja generator urejenih celih števil (sekvenca).
DDL prostora izgleda tako:

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, pred zagonem benchmarka, se prostor napolni z 10.000 tupli v obliki

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

Dostop do tuple se izvaja preko naključne vrednosti ključa.

Sam benchmark predstavlja en sam zahtevek strežniku, ki se izvede 10.000-krat (revolucij), ki se, za redom, izvajajo v iteracijah. Iteracije se ponavljajo, dokler vse odstopanja v času med 5 iteracijami ne bodo v okviru dopustnega odklona 3 %*. Po tem se vzame povprečen rezultat. Med iteracijami je premor 1 sekundo, da se procesor ne prepusti throttlingu. Zbiralnik odpadkov Lua se izklopi pred vsakim iteracijo in se prisilno zažene po njenem zaključku. PHP-postopek se zažene le z nujnimi razširitvami za benchmark, z vklopljeno predpomnilnikom izhoda in izklopljenim zbiralnikom odpadkov.

* Število revolucij, iteracij in prag napake je mogoče spremeniti v nastavitvah benchmarka.

Testno okolje

Objavljeni rezultati so bili narejeni na MacBookPro (2015), operacijski sistem — Fedora 30 (različica jedra 5.3.8-200.fc30.x86_64). Tarantool se je zagnal v Dockerju s parametrom „--network host".

Različice paketov:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (zgrajen: 22. okt 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

* Žal, uradni konektor ne deluje z različico PHP > 7.2. Da bi lahko sestavili in zagnali razširitev na PHP 7.3, je bilo treba uporabiti patchom.

Rezultati

Sinhroni način

Protokol Tarantoola uporablja binarno obliko MessagePack za serializacijo sporočil. V konektorju PECL je serializacija globoko skrita v nedrih knjižnice in vplivati na postopek kodiranja iz userland-kode ni mogoče.. PHP Connector, nasprotno, omogoča prilagoditev procesa kodiranja z razširitvijo standardnega kodirnika ali možnostjo uporabe lastne implementacije. Izključeno sta na voljo dva kodirnika, eden temelji na msgpack/msgpack-php (uradna razširitev MessagePack PECL), drugi pa na rybakit/msgpack (v čistem PHP).

Pred primerjavo konektorjev bomo izmerili zmogljivost MessagePack kodirnikov za PHP konektor in v nadaljnjih testih bomo uporabili tistega, ki bo pokazal najboljše rezultate:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Čeprav PHP različica (Pure) zaostaja za PECL razširitvijo po hitrosti, bi kljub temu v realnih projektih priporočil uporabo rybakit/msgpack, ker uradna razširitev MessagePack implementira specifikacijo formata le delno (na primer, ne podpira uporabniških tipov podatkov, brez katerih ne boste mogli uporabljati Decimal — novega tipa podatkov, predstavljenega v Tarantool 2.3) in ima vrsto drugih problémech (vključno s težavami s združljivostjo s PHP 7.4). Na splošno pa projekt deluje zapostavljeno.

Torej, izmerimo zmogljivost konektorjev v sinhronem načinu:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Kot je razvidno iz grafa, PECL konektor (Tarantool) kaže boljšo zmogljivost v primerjavi s PHP konektorjem (Client). A to ni presenetljivo, če upoštevamo, da ta, poleg tega, da je implementiran v počasnejšem jeziku, izvaja tudi več dela: pri vsakem klicu se ustvari nov objekt Request in Odgovor (v primeru Select — še tudi Criteria, medtem ko v primeru Update/Upsert — Operations), posamezne entitete Povezava, Packer in Handler tudi dodajajo ovire. Očitno je, da za prilagodljivost plačamo. Vendar pa na splošno PHP interpreter kaže dobro zmogljivost, čeprav razlika obstaja, a je zanemarljiva in bo morda še manjša pri uporabi preloading v PHP 7.4, da ne omenjam JIT v PHP 8.

Nadaljujemo. V Tarantool 2.0 je prišla podpora za SQL. Poskusimo izvesti operacije Select, Insert, Update in Delete z uporabo SQL protokola in primerjati rezultate z noSQL (binarno) ekvivalenti:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Rezultati SQL niso preveč impresivni (spomnim vas, da še vedno testiramo sinhroni način). Vendar pa se ne bi smel prehitro obupati, saj je podpora za SQL še vedno v aktivnem razvoju (pred kratkim, na primer, je bila dodana podpora prepared statements) in, soditi po seznamu issues, SQL engine is expected to undergo a series of optimizations.

Async

Well, let's see how the Async extension can help us improve the above results. To write asynchronous programs, the extension provides an API based on coroutines, which we will use. Through experimentation, we find that the optimal number of coroutines for our environment is 25:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
We will "spread out" 10,000 operations across 25 coroutines and see what we get:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
The number of operations per second has increased more than 3 times for tarantool-php/client!

Unfortunately, the PECL connector did not start with ext-async.

And what about SQL?

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
As you can see, in asynchronous mode, the difference between the binary protocol and SQL is within the margin of error.

Swoole

Again, we discover the optimal number of coroutines, this time for Swoole:
Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
We will stick to 25. We'll repeat the same trick as with the Async extension — distributing 10,000 operations across 25 coroutines. In addition, we'll add another test where we divide all the work into 2 processes (meaning each process will execute 5,000 operations in 25 coroutines). The processes will be created using SwooleProcess.

Rezultati:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Swole shows a slightly lower result compared to Async when running in a single process, but with 2 processes, the picture changes dramatically (the number 2 is not chosen randomly; on my machine, exactly 2 processes showed the best result).

By the way, the Async extension also has an API for working with processes, but I did not notice any difference in running benchmarks in one or several processes (it's possible that I messed something up).

SQL vs binary protocol:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Just like with Async, the difference between binary and SQL operations is leveling out in asynchronous mode.

Parallel

Since the Parallel extension is not about coroutines but about threads, let's measure the optimal number of parallel threads:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
It's 16 on my machine. Let's run connector benchmarks with 16 parallel threads:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
As you can see, the result is even better than with asynchronous extensions (except for Swoole running on 2 processes). Note that for the PECL connector, there is nothing in the update and upsert operations. This is related to the fact that these operations failed with an error — I do not know whether it was due to ext-parallel, ext-tarantool, or both.

Now let's compare SQL performance:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel
Notice the similarity with the graphs for connectors running synchronously?

All together

In the end, let's compile all the results into a single graph to see the overall picture for the tested extensions. We will add just one new test to the graph that we haven't done yet — we'll run Async coroutines in parallel using Parallel*. The idea of integrating the aforementioned extensions has already been discussed by the authors, but a consensus was never reached, so we will have to do it ourselves.

* Running Swoole coroutines with Parallel didn't work; it seems these extensions are incompatible.

So, the final results are:

Pospešujemo PHP konektorje za Tarantool z Async, Swoole in Parallel

Namesto zaključka

In my opinion, the results are quite impressive, and for some reason, I believe this is still not the limit! Whether you need this in a real project is entirely up to you; I will only say that for me it was an interesting experiment that allowed me to assess how much you can "squeeze" from a synchronous TCP connector with minimal effort. If you have ideas for improving the benchmarks — I would be happy to consider your pull request. All the code with instructions for running and results is published separately repozitoriji.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster