Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel

I PHP-økosystemet er der i øjeblikket to stik til at arbejde med Tarantool-serveren - dette er den officielle PECL-udvidelse tarantool/tarantool-php, skrevet i C, og tarantool-php/klient, skrevet i PHP. Jeg er forfatter til sidstnævnte.

I denne artikel vil jeg gerne dele resultaterne af ydelsestest af begge biblioteker og vise, hvordan du med minimale ændringer i koden kan opnå en 3-5 ydelsesforøgelse (på syntetiske tests!).

Hvad vil vi teste?

Vi vil teste dem, der er nævnt ovenfor synkron stik, der kører asynkront, parallelt og asynkront-parallelt. 🙂 Vi ønsker heller ikke at røre ved selve stikkets kode. Der er i øjeblikket flere udvidelser tilgængelige for at opnå det, du ønsker:

  • Swolle ― en højtydende asynkron ramme til PHP. Brugt af sådanne internetgiganter som Alibaba og Baidu. Siden version 4.1.0 er der dukket en magisk metode op SwooleRuntime::enableCoroutine(), som giver dig mulighed for at "konvertere synkrone PHP-netværksbiblioteker til asynkrone med en linje kode."
  • Async var indtil for nylig en meget lovende udvidelse til asynkront arbejde i PHP. Hvorfor indtil for nylig? Desværre, af en for mig ukendt årsag, slettede forfatteren depotet, og projektets fremtidige skæbne er uklar. Jeg bliver nødt til at bruge den en fra gafler. Ligesom Swoole giver denne udvidelse dig mulighed for nemt at tænde dine bukser med et svirp med håndleddet for at aktivere asynkroni ved at erstatte standardimplementeringen af ​​TCP- og TLS-streams med deres asynkrone versioner. Dette gøres gennem muligheden "async.tcp = 1".
  • Parallel ― en ret ny udvidelse fra den velkendte Joe Watkins, forfatter til sådanne biblioteker som phpdbg, apcu, pthreads, pcov, uopz. Udvidelsen giver et API til multithreading i PHP og er placeret som en erstatning for pthreads. En væsentlig begrænsning ved biblioteket er, at det kun virker med ZTS (Zend Thread Safe) versionen af ​​PHP.

Hvordan vil vi teste?

Lad os lancere en Tarantool-forekomst med deaktiveret skrive-forud-logning (wal_mode = ingen) og øget netværksbuffer (readahead = 1 * 1024 * 1024). Den første mulighed vil eliminere arbejde med disken, den anden vil gøre det muligt at læse flere anmodninger fra operativsystemets buffer og derved minimere antallet af systemopkald.

For benchmarks, der arbejder med data (indsættelse, sletning, læsning osv.), vil der før start af benchmark blive (gen)oprettet et memtx-rum, hvor de primære indeksværdier skabes af en generator af ordnede heltalsværdier ​(sekvens).
Space DDL ser sådan ud:

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

Om nødvendigt, før benchmark køres, er pladsen fyldt med 10,000 tuples af formularen

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

Tuples tilgås ved hjælp af en tilfældig nøgleværdi.

Selve benchmark er en enkelt anmodning til serveren, som udføres 10,000 gange (omdrejninger), som igen udføres i iterationer. Iterationerne gentages, indtil alle tidsafvigelser mellem 5 iterationer er inden for en acceptabel fejl på 3 %*. Herefter tages gennemsnitsresultatet. Der er en pause på 1 sekund mellem iterationerne for at forhindre processoren i at drosle. Luas skraldeopsamler deaktiveres før hver iteration og er tvunget til at starte, når den er fuldført. PHP-processen lanceres kun med de udvidelser, der er nødvendige for benchmark, med outputbuffering aktiveret og garbage collector deaktiveret.

* Antallet af omdrejninger, iterationer og fejltærskel kan ændres i benchmarkindstillingerne.

Test miljø

Resultaterne offentliggjort nedenfor blev lavet på et MacBookPro (2015), operativsystem - Fedora 30 (kerneversion 5.3.8-200.fc30.x86_64). Tarantool blev lanceret i docker med parameteren "--network host".

Pakkeversioner:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, byg a872fc2f86
PHP: 7.3.11 (cli) (bygget: 22. oktober 2019 08:11:04)
tarantool/klient: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch til 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swool: 4.4.12
ext-parallel: 1.1.3

* Desværre virker det officielle stik ikke med PHP version > 7.2. For at kompilere og køre udvidelsen på PHP 7.3, var jeg nødt til at bruge lappe.

Fund

Synkron tilstand

Tarantool-protokollen bruger et binært format MessagePack at serialisere meddelelser. I PECL-stikket er serialisering skjult dybt i dybden af ​​biblioteket og påvirker kodningsprocessen fra brugerlandskode synes ikke muligt. En ren PHP-connector giver tværtimod mulighed for at tilpasse kodningsprocessen ved at udvide standardkoderen eller ved at bruge din egen implementering. Der er to encodere tilgængelige ud af æsken, den ene er baseret på msgpack/msgpack-php (officiel MessagePack PECL-udvidelse), den anden er tændt rybakit/msgpack (i ren PHP).

Før vi sammenligner stik, vil vi måle ydeevnen af ​​MessagePack-kodere til PHP-stikket, og i yderligere test vil vi bruge den, der viser det bedste resultat:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Selvom PHP-versionen (Pure) er ringere end PECL-udvidelsen i hastighed, vil jeg i rigtige projekter stadig anbefale at bruge den rybakit/msgpack, fordi i den officielle MessagePack-udvidelse er formatspecifikationen kun delvist implementeret (for eksempel er der ingen understøttelse af brugerdefinerede datatyper, uden hvilke du ikke vil være i stand til at bruge Decimal - en ny datatype introduceret i Tarantool 2.3) og har en antal andre problemer (inklusive kompatibilitetsproblemer med PHP 7.4). Nå, generelt ser projektet forladt ud.

Så lad os måle ydeevnen af ​​stik i synkron tilstand:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Som det kan ses af grafen, viser PECL-stikket (Tarantool) bedre ydeevne sammenlignet med PHP-stikket (Client). Men det er ikke overraskende, da sidstnævnte, ud over at være implementeret i et langsommere sprog, faktisk gør mere arbejde: et nyt objekt oprettes med hvert kald Anmod om и Respons (i tilfælde af Select - også Kriterier, og i tilfælde af Update/Upsert ― Produktion), separate enheder Forbindelse, Packer и handler de tilføjer også overhead. Fleksibilitet har naturligvis en pris. Men generelt viser PHP-fortolkeren god ydeevne, selvom der er en forskel, er den ubetydelig og vil måske være endnu mindre, når du bruger preloading i PHP 7.4, for ikke at nævne JIT i PHP 8.

Lad os gå videre. Tarantool 2.0 introducerede SQL-understøttelse. Lad os prøve at udføre Select, Insert, Update og Delete operationer ved hjælp af SQL-protokollen og sammenligne resultaterne med noSQL (binære) ækvivalenter:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
SQL-resultaterne er ikke særlig imponerende (lad mig minde dig om, at vi stadig tester synkron tilstand). Jeg ville dog ikke blive ked af det på forhånd; SQL-support er stadig under aktiv udvikling (relativt for nylig blev der f.eks. tilføjet support udarbejdede erklæringer) og efter listen at dømme spørgsmål, vil SQL-motoren gennemgå en række optimeringer i fremtiden.

asynkron

Nå, lad os nu se, hvordan Async-udvidelsen kan hjælpe os med at forbedre resultaterne ovenfor. For at skrive asynkrone programmer giver udvidelsen en API baseret på coroutines, som vi vil bruge. Vi finder empirisk ud af, at det optimale antal koroutiner for vores miljø er 25:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
"Fordel" 10,000 operationer på tværs af 25 coroutiner og se, hvad der sker:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Antallet af operationer i sekundet steg med mere end 3 gange for tarantool-php/klient!

Desværre startede PECL-stikket ikke med ext-async.

Hvad med SQL?

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Som du kan se, var forskellen mellem den binære protokol og SQL i asynkron tilstand inden for fejlmarginen.

Swolle

Igen finder vi ud af det optimale antal koroutiner, denne gang for Swoole:
Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Lad os stoppe ved 25. Lad os gentage det samme trick som med Async-udvidelsen - fordel 10,000 operationer mellem 25 coroutiner. Derudover vil vi tilføje endnu en test, hvor vi vil opdele alt arbejdet i 2 to processer (det vil sige, at hver proces vil udføre 5,000 operationer i 25 coroutiner). Processer vil blive oprettet vha SwooleProcess.

resultater:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Swole viser et lidt lavere resultat sammenlignet med Async, når det køres i én proces, men med 2 processer ændrer billedet sig dramatisk (tallet 2 blev ikke valgt tilfældigt; på min maskine var det 2 processer, der viste det bedste resultat).

Async-udvidelsen har i øvrigt også en API til at arbejde med processer, men der mærkede jeg ingen forskel fra at køre benchmarks i en eller flere processer (det er muligt, at jeg har rodet et sted).

SQL vs binær protokol:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Som med Async elimineres forskellen mellem binære og SQL-operationer i asynkron tilstand.

Parallel

Da den parallelle udvidelse ikke handler om koroutiner, men om tråde, lad os måle det optimale antal parallelle tråde:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Det er lig med 16 på min maskine. Lad os køre stikbenchmarks på 16 parallelle tråde:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Som du kan se, er resultatet endnu bedre end med asynkrone udvidelser (bortset fra Swoole, der kører på 2 processer). Bemærk, at for PECL-stikket er opdaterings- og upsert-operationerne tomme. Dette skyldes det faktum, at disse operationer mislykkedes med en fejl - jeg ved ikke, om det var fejlen af ​​ext-parallel, ext-tarantool eller begge dele.

Lad os nu sammenligne SQL-ydeevne:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel
Bemærk ligheden med grafen for stik, der kører synkront?

Sammen

Og endelig, lad os opsummere alle resultaterne i én graf for at se det overordnede billede for de testede udvidelser. Lad os blot tilføje en ny test til diagrammet, som vi ikke har lavet endnu - lad os køre Async-koroutiner parallelt ved hjælp af Parallel*. Ideen om at integrere ovenstående udvidelser er allerede diskuteret forfattere, men der blev ikke opnået konsensus, du bliver nødt til at gøre det selv.

* Det var ikke muligt at lancere Swoole coroutines med Parallel; det ser ud til, at disse udvidelser er inkompatible.

Så de endelige resultater:

Accelererende PHP-stik til Tarantool ved hjælp af Async, Swoole og Parallel

I stedet for en konklusion

Efter min mening viste resultaterne sig at være ret værdige, og af en eller anden grund er jeg sikker på, at dette ikke er grænsen! Om du skal bestemme dette i et rigtigt projekt udelukkende for dig selv, vil jeg kun sige, at for mig var det et interessant eksperiment, der giver dig mulighed for at evaluere, hvor meget du kan "presse" ud af et synkront TCP-stik med minimal indsats. Hvis du har ideer til at forbedre benchmarks, vil jeg med glæde overveje din pull-anmodning. Al kode med lanceringsinstruktioner og resultater offentliggøres i en separat depoter.

Kilde: www.habr.com

Tilføj en kommentar