Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel

I PHP-økosystemet er det for tiden to kontakter for å jobbe med Tarantool-serveren - dette er den offisielle PECL-utvidelsen tarantool/tarantool-php, skrevet i C, og tarantool-php/klient, skrevet i PHP. Jeg er forfatteren av sistnevnte.

I denne artikkelen vil jeg dele resultatene av ytelsestesting av begge bibliotekene og vise hvordan du, med minimale endringer i koden, kan oppnå en ytelsesøkning på 3-5 (på syntetiske tester!).

Hva skal vi teste?

Vi vil teste de som er nevnt ovenfor synkron kontakter som kjører asynkront, parallelt og asynkront-parallelt. 🙂 Vi ønsker heller ikke å berøre koden til selve kontaktene. Det er for øyeblikket flere utvidelser tilgjengelig for å oppnå det du ønsker:

  • Swoole ― et høyytelses asynkront rammeverk for PHP. Brukt av slike internettgiganter som Alibaba og Baidu. Siden versjon 4.1.0 har en magisk metode dukket opp SwooleRuntime::enableCoroutine(), som lar deg "konvertere synkrone PHP-nettverksbiblioteker til asynkrone med én kodelinje."
  • Async var inntil nylig en meget lovende utvidelse for asynkront arbeid i PHP. Hvorfor inntil nylig? Dessverre, av en for meg ukjent grunn, slettet forfatteren depotet, og den fremtidige skjebnen til prosjektet er uklar. Jeg må bruke den en fra gafler. I likhet med Swoole lar denne utvidelsen deg enkelt slå på buksene dine med et håndleddsgrep for å aktivere asynkroni ved å erstatte standardimplementeringen av TCP- og TLS-strømmer med deres asynkrone versjoner. Dette gjøres gjennom alternativet "async.tcp = 1".
  • Parallel ― en ganske ny utvidelse fra den velkjente Joe Watkins, forfatter av slike biblioteker som phpdbg, apcu, pthreads, pcov, uopz. Utvidelsen gir et API for multithreading i PHP og er posisjonert som en erstatning for pthreads. En betydelig begrensning ved biblioteket er at det bare fungerer med ZTS (Zend Thread Safe) versjonen av PHP.

Hvordan skal vi teste?

La oss lansere en Tarantool-forekomst med loggføring for skriving deaktivert (wal_mode = ingen) og økt nettverksbuffer (avlesning = 1 * 1024 * 1024). Det første alternativet vil eliminere arbeid med disken, det andre vil gjøre det mulig å lese flere forespørsler fra operativsystembufferen og dermed minimere antall systemanrop.

For benchmarks som fungerer med data (innsetting, sletting, lesing, etc.), før start av benchmark, vil et memtx-rom bli (re)laget, der de primære indeksverdiene opprettes av en generator av ordnede heltallsverdier ​(sekvens).
Space DDL ser slik ut:

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ødvendig, før du kjører benchmark, fylles plassen med 10,000 XNUMX tupler av skjemaet

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

Tupler er tilgjengelig ved hjelp av en tilfeldig nøkkelverdi.

Selve referansen er en enkelt forespørsel til serveren, som utføres 10,000 5 ganger (revolusjoner), som i sin tur utføres i iterasjoner. Iterasjonene gjentas til alle tidsavvik mellom 3 iterasjoner er innenfor en akseptabel feil på 1 %*. Etter dette tas gjennomsnittsresultatet. Det er XNUMX sekunds pause mellom iterasjonene for å forhindre at prosessoren struper. Luas søppelsamler er deaktivert før hver iterasjon og blir tvunget til å starte etter at den er fullført. PHP-prosessen lanseres kun med utvidelsene som er nødvendige for referansen, med utgangsbuffring aktivert og søppelsamleren deaktivert.

* Antall omdreininger, iterasjoner og feilterskel kan endres i benchmarkinnstillingene.

Test miljø

Resultatene publisert nedenfor ble laget på et MacBookPro (2015), operativsystem - Fedora 30 (kjerneversjon 5.3.8-200.fc30.x86_64). Tarantool ble lansert i docker med parameteren "--network host".

Pakkeversjoner:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, bygg a872fc2f86
PHP: 7.3.11 (cli) (bygget: 22. oktober 2019 08:11:04)
tarantverktøy/klient: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch for 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
utvendig sull: 4.4.12
ext-parallell: 1.1.3

* Dessverre fungerer ikke den offisielle koblingen med PHP-versjon > 7.2. For å kompilere og kjøre utvidelsen på PHP 7.3, måtte jeg bruke lapp.

Funn

Synkron modus

Tarantool-protokollen bruker et binært format MessagePack å serialisere meldinger. I PECL-kontakten er serialisering gjemt dypt i dypet av biblioteket og påvirker kodingsprosessen fra brukerlandskode virker ikke mulig. En ren PHP-kobling gir tvert imot muligheten til å tilpasse kodingsprosessen ved å utvide standardkoderen eller ved å bruke din egen implementering. Det er to kodere tilgjengelig ut av esken, en er basert på msgpack/msgpack-php (offisiell MessagePack PECL-utvidelse), den andre er på rybakit/msgpack (i ren PHP).

Før vi sammenligner koblinger, vil vi måle ytelsen til MessagePack-kodere for PHP-koblingen, og i videre tester vil vi bruke den som viser det beste resultatet:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Selv om PHP-versjonen (Pure) er dårligere enn PECL-utvidelsen i hastighet, vil jeg i virkelige prosjekter fortsatt anbefale å bruke den rybakit/msgpack, fordi i den offisielle MessagePack-utvidelsen er formatspesifikasjonen bare delvis implementert (for eksempel er det ingen støtte for tilpassede datatyper, uten hvilke du ikke vil kunne bruke Desimal - en ny datatype introdusert i Tarantool 2.3) og har en antall andre problemer (inkludert kompatibilitetsproblemer med PHP 7.4). Vel, generelt ser prosjektet forlatt ut.

Så la oss måle ytelsen til koblinger i synkron modus:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Som man kan se av grafen viser PECL-kontakten (Tarantool) bedre ytelse sammenlignet med PHP-kontakten (klient). Men dette er ikke overraskende, gitt at sistnevnte, i tillegg til å være implementert på et tregere språk, faktisk gjør mer arbeid: et nytt objekt opprettes med hver samtale Be и Respons (i tilfelle av Select - også Kriterier, og i tilfelle Update/Upsert ― Drift), separate enheter Tilkobling, Packer и Handler de legger også til overhead. Fleksibilitet har selvsagt en pris. Men generelt viser PHP-tolken god ytelse, selv om det er en forskjell, er den ubetydelig og vil kanskje være enda mindre når du bruker forhåndslasting i PHP 7.4, for ikke å nevne JIT i PHP 8.

La oss gå videre. Tarantool 2.0 introduserte SQL-støtte. La oss prøve å utføre Select, Insert, Update og Delete-operasjoner ved å bruke SQL-protokollen og sammenligne resultatene med noSQL (binære) ekvivalenter:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
SQL-resultatene er ikke veldig imponerende (la meg minne deg på at vi fortsatt tester synkronmodus). Jeg ville imidlertid ikke bli opprørt over dette på forhånd; SQL-støtte er fortsatt under aktiv utvikling (relativt nylig ble for eksempel støtte lagt til utarbeidet uttalelser) og, etter listen å dømme saker, vil SQL-motoren gjennomgå en rekke optimaliseringer i fremtiden.

Asynkronisering

Vel, la oss nå se hvordan Async-utvidelsen kan hjelpe oss med å forbedre resultatene ovenfor. For å skrive asynkrone programmer gir utvidelsen en API basert på korutiner, som vi vil bruke. Vi finner empirisk ut at det optimale antallet koroutiner for miljøet vårt er 25:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
"Sprede" 10,000 25 operasjoner på XNUMX koroutiner og se hva som skjer:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Antall operasjoner per sekund økte med mer enn 3 ganger for tarantool-php/klient!

Dessverre startet ikke PECL-kontakten med ext-async.

Hva med SQL?

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Som du kan se, i asynkron modus ble forskjellen mellom den binære protokollen og SQL innenfor feilmarginen.

Swoole

Igjen finner vi ut det optimale antallet koroutiner, denne gangen for Swoole:
Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
La oss stoppe ved 25. La oss gjenta det samme trikset som med Async-utvidelsen – fordel 10,000 25 operasjoner mellom 2 koroutiner. I tillegg vil vi legge til en annen test der vi deler alt arbeidet i 5,000 to prosesser (det vil si at hver prosess vil utføre 25 operasjoner i XNUMX koroutiner). Prosesser vil bli opprettet ved hjelp av SwooleProcess.

Resultater:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Swole viser et litt lavere resultat sammenlignet med Async når det kjøres i én prosess, men med 2 prosesser endrer bildet seg dramatisk (tallet 2 ble ikke valgt ved en tilfeldighet; på min maskin var det 2 prosesser som viste best resultat).

Async-utvidelsen har forresten også en API for å jobbe med prosesser, men der merket jeg ingen forskjell fra å kjøre benchmarks i en eller flere prosesser (det er mulig jeg rotet til et sted).

SQL vs binær protokoll:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Som med Async, elimineres forskjellen mellom binære og SQL-operasjoner i asynkron modus.

Parallel

Siden den parallelle utvidelsen ikke handler om korutiner, men om tråder, la oss måle det optimale antallet parallelle tråder:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Det er 16 på maskinen min. La oss kjøre koblingsreferanser på 16 parallelle tråder:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Som du kan se, er resultatet enda bedre enn med asynkrone utvidelser (ikke teller Swoole som kjører på 2 prosesser). Merk at for PECL-kontakten er oppdaterings- og oppgraderingsoperasjonene tomme. Dette skyldes det faktum at disse operasjonene mislyktes med en feil - jeg vet ikke om det var feilen til ext-parallel, ext-tarantool eller begge deler.

La oss nå sammenligne SQL-ytelse:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel
Legger du merke til likheten med grafen for koblinger som kjører synkront?

Sammen

Og til slutt, la oss oppsummere alle resultatene i én graf for å se helhetsbildet for de testede utvidelsene. La oss bare legge til en ny test til diagrammet, som vi ikke har gjort ennå – la oss kjøre Async-korutiner parallelt ved å bruke Parallel*. Ideen om å integrere de ovennevnte utvidelsene er allerede ble diskutert forfattere, men det ble ikke oppnådd enighet, du må gjøre det selv.

* Det var ikke mulig å lansere Swoole-koroutiner med Parallel; det ser ut til at disse utvidelsene er inkompatible.

Så, de endelige resultatene:

Akselererende PHP-kontakter for Tarantool ved hjelp av Async, Swoole og Parallel

I stedet for en konklusjon

Etter min mening viste resultatene seg å være ganske verdige, og av en eller annen grunn er jeg sikker på at dette ikke er grensen! Enten du trenger å bestemme dette i et ekte prosjekt utelukkende for deg selv, vil jeg bare si at for meg var det et interessant eksperiment som lar deg evaluere hvor mye du kan "klemme" ut av en synkron TCP-kontakt med minimal innsats. Hvis du har ideer for å forbedre benchmarks, vil jeg gjerne vurdere pull-forespørselen din. All kode med lanseringsinstruksjoner og resultater publiseres i en egen depoter.

Kilde: www.habr.com

Legg til en kommentar