Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel

I PHP-ekosystemet finns det för närvarande två kontakter för att arbeta med Tarantool-servern - detta är den officiella PECL-tillägget tarantool/tarantool-php, skrivet i C, och tarantool-php/klient, skrivet i PHP. Jag är författare till det sistnämnda.

I den här artikeln skulle jag vilja dela resultaten av prestandatestning av båda biblioteken och visa hur du, med minimala ändringar av koden, kan uppnå en 3-5 prestandaökning (på syntetiska tester!).

Vad ska vi testa?

Vi kommer att testa de som nämns ovan synkron kontakter som körs asynkront, parallellt och asynkront-parallellt. 🙂 Vi vill inte heller röra koden för själva kontakterna. Det finns för närvarande flera tillägg tillgängliga för att uppnå det du vill:

  • Swoole ― ett högpresterande asynkront ramverk för PHP. Används av sådana internetjättar som Alibaba och Baidu. Sedan version 4.1.0 har en magisk metod dykt upp SwooleRuntime::enableCoroutine(), som låter dig "konvertera synkrona PHP-nätverksbibliotek till asynkrona med en rad kod."
  • Async var tills nyligen en mycket lovande tillägg för asynkront arbete i PHP. Varför tills nyligen? Tyvärr, av en för mig okänd anledning, tog författaren bort arkivet och projektets framtida öde är oklart. Jag måste använda den en från gafflar. Precis som Swoole låter denna förlängning dig enkelt sätta på dina byxor med ett handgrepp för att möjliggöra asynkron genom att ersätta standardimplementeringen av TCP- och TLS-strömmar med deras asynkrona versioner. Detta görs genom alternativet "async.tcp = 1".
  • Parallell ― en ganska ny tillägg från den välkända Joe Watkins, författare till sådana bibliotek som phpdbg, apcu, pthreads, pcov, uopz. Tillägget tillhandahåller ett API för multithreading i PHP och är placerat som en ersättning för pthreads. En betydande begränsning av biblioteket är att det bara fungerar med ZTS-versionen (Zend Thread Safe) av PHP.

Hur ska vi testa?

Låt oss lansera en Tarantool-instans med skrive-förut-loggning inaktiverad (wal_mode = ingen) och ökad nätverksbuffert (readahead = 1 * 1024 * 1024). Det första alternativet kommer att eliminera arbete med disken, det andra gör det möjligt att läsa fler förfrågningar från operativsystemets buffert och därigenom minimera antalet systemanrop.

För benchmarks som fungerar med data (infogning, radering, läsning, etc.), innan benchmark startar, kommer ett memtx-utrymme att (åter)skapas, där de primära indexvärdena skapas av en generator av ordnade heltalsvärden ​(sekvens).
Utrymmet DDL ser ut så här:

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 det behövs, innan riktmärket körs, fylls utrymmet med 10,000 XNUMX tuplar av formuläret

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

Tuples nås med ett slumpmässigt nyckelvärde.

Själva riktmärket är en enda begäran till servern, som exekveras 10,000 5 gånger (varv), som i sin tur exekveras i iterationer. Iterationerna upprepas tills alla tidsavvikelser mellan 3 iterationer ligger inom ett acceptabelt fel på 1 %*. Efter detta tas det genomsnittliga resultatet. Det finns en paus på XNUMX sekund mellan iterationerna för att förhindra att processorn stryper. Luas sopsamlare inaktiveras före varje iteration och tvingas starta efter att den är klar. PHP-processen startas endast med de tillägg som krävs för riktmärket, med outputbuffring aktiverad och garbage collector inaktiverad.

* Antalet varv, iterationer och feltröskel kan ändras i benchmarkinställningarna.

Testmiljö

Resultaten som publiceras nedan gjordes på ett MacBookPro (2015), operativsystem - Fedora 30 (kärnversion 5.3.8-200.fc30.x86_64). Tarantool lanserades i docker med parametern "--network host".

Paketversioner:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, bygg a872fc2f86
PHP: 7.3.11 (cli) (byggd: 22 oktober 2019 08:11:04)
tarantverktyg/klient: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch för 7.3)*
ext-msgpack: 2.0.3
ext-asynkron: 0.3.0-8c1da46
ext-swool: 4.4.12
ext-parallell: 1.1.3

* Tyvärr fungerar inte den officiella anslutningen med PHP-version > 7.2. För att kompilera och köra tillägget på PHP 7.3 var jag tvungen att använda lappa.

Resultat

Synkront läge

Tarantool-protokollet använder ett binärt format MessagePack för att serialisera meddelanden. I PECL-kontakten är serialisering gömd djupt i bibliotekets djup och påverkar kodningsprocessen från användarlandskod verkar inte möjligt. En ren PHP-kontakt ger tvärtom möjligheten att anpassa kodningsprocessen genom att utöka standardkodaren eller genom att använda din egen implementering. Det finns två kodare tillgängliga direkt från förpackningen, en är baserad på msgpack/msgpack-php (officiell MessagePack PECL-tillägg), den andra är på rybakit/msgpack (i ren PHP).

Innan vi jämför anslutningar kommer vi att mäta prestandan hos MessagePack-kodare för PHP-kontakten och i ytterligare tester kommer vi att använda den som visar bäst resultat:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Även om PHP-versionen (Pure) är sämre än PECL-förlängningen i hastighet, i verkliga projekt skulle jag fortfarande rekommendera att använda den rybakit/msgpack, för i den officiella MessagePack-tillägget är formatspecifikationen endast delvis implementerad (till exempel finns det inget stöd för anpassade datatyper, utan vilka du inte kommer att kunna använda Decimal - en ny datatyp som introduceras i Tarantool 2.3) och har en antal andra problem (inklusive kompatibilitetsproblem med PHP 7.4). Tja, i allmänhet ser projektet övergivet ut.

Så låt oss mäta prestanda för kontakter i synkront läge:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Som framgår av grafen visar PECL-kontakten (Tarantool) bättre prestanda jämfört med PHP-kontakten (klient). Men detta är inte förvånande, med tanke på att det senare, förutom att vara implementerat på ett långsammare språk, faktiskt gör mer arbete: ett nytt objekt skapas med varje anrop FÖRFRÅGAN и Svar (i fallet med Välj - också Kriterier, och i fallet med Update/Upsert ― Verksamhet), separata enheter förbindelse, Packer и Handler de lägger också till overhead. Självklart har flexibilitet ett pris. Men generellt sett visar PHP-tolken bra prestanda, även om det finns en skillnad är den obetydlig och kanske ännu mindre när man använder förladdning i PHP 7.4, för att inte tala om JIT i PHP 8.

Låt oss gå vidare. Tarantool 2.0 lade till stöd för SQL. Låt oss försöka utföra operationerna Välj, Infoga, Uppdatera och Ta bort med SQL-protokollet och jämför resultaten med noSQL (binära) motsvarigheter:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
SQL-resultaten är inte särskilt imponerande (låt mig påminna dig om att vi fortfarande testar synkront läge). Jag skulle dock inte bli upprörd över detta i förväg; SQL-stödet är fortfarande under aktiv utveckling (relativt nyligen har till exempel support lagts till utarbetade uttalanden) och, att döma av listan problem, kommer SQL-motorn att genomgå ett antal optimeringar i framtiden.

Asynk

Nåväl, låt oss nu se hur Async-tillägget kan hjälpa oss att förbättra resultaten ovan. För att skriva asynkrona program tillhandahåller tillägget ett API baserat på coroutines, som vi kommer att använda. Vi får empiriskt reda på att det optimala antalet koroutiner för vår miljö är 25:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
"Fördela" 10,000 25 operationer över XNUMX koroutiner och se vad som händer:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Antalet operationer per sekund ökade med mer än 3 gånger för tarantool-php/klient!

Tyvärr startade inte PECL-kontakten med ext-async.

Hur är det med SQL?

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Som du kan se, i asynkront läge blev skillnaden mellan det binära protokollet och SQL inom felmarginalen.

Swoole

Återigen tar vi reda på det optimala antalet koroutiner, denna gång för Swoole:
Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Låt oss stanna vid 25. Låt oss upprepa samma trick som med tillägget Async - fördela 10,000 25 operationer mellan 2 koroutiner. Dessutom kommer vi att lägga till ytterligare ett test där vi delar upp allt arbete i 5,000 två processer (det vill säga varje process kommer att utföra 25 XNUMX operationer i XNUMX koroutiner). Processer kommer att skapas med hjälp av SwooleProcess.

resultat:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Swole visar ett något lägre resultat jämfört med Async när det körs i en process, men med 2 processer förändras bilden dramatiskt (siffran 2 valdes inte av en slump; på min maskin var det 2 processer som visade det bästa resultatet).

Async-tillägget har för övrigt också ett API för att arbeta med processer, men där märkte jag ingen skillnad mot att köra benchmarks i en eller flera processer (det är möjligt att jag trasslat till någonstans).

SQL vs binärt protokoll:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Liksom med Async elimineras skillnaden mellan binära och SQL-operationer i asynkront läge.

Parallell

Eftersom den parallella förlängningen inte handlar om koroutiner, utan om trådar, låt oss mäta det optimala antalet parallella trådar:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Det är lika med 16 på min maskin. Låt oss köra anslutningsriktmärken på 16 parallella trådar:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Som du kan se är resultatet ännu bättre än med asynkrona förlängningar (inte räknar Swoole som körs på 2 processer). Observera att för PECL-kontakten är uppdaterings- och upsert-operationerna tomma. Detta beror på det faktum att dessa operationer misslyckades med ett fel - jag vet inte om det var felet på ext-parallel, ext-tarantool eller båda.

Låt oss nu jämföra SQL-prestanda:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel
Lägger du märke till likheten med grafen för kontakter som körs synkront?

Tillsammans

Och slutligen, låt oss sammanfatta alla resultat i en graf för att se den övergripande bilden för de testade tilläggen. Låt oss bara lägga till ett nytt test i diagrammet, vilket vi inte har gjort ännu - låt oss köra Async-korutiner parallellt med Parallel*. Tanken med att integrera ovanstående tillägg finns redan diskuteras författare, men ingen konsensus nåddes, du måste göra det själv.

* Det var inte möjligt att lansera Swoole coroutines med Parallel, det verkar som om dessa tillägg är inkompatibla.

Så, slutresultatet:

Accelererande PHP-kontakter för Tarantool med Async, Swoole och Parallel

I stället för en slutsats

Enligt min mening visade sig resultaten vara ganska värda, och av någon anledning är jag säker på att detta inte är gränsen! Oavsett om du behöver bestämma detta i ett riktigt projekt enbart för dig själv, jag kommer bara att säga att för mig var det ett intressant experiment som låter dig utvärdera hur mycket du kan "pressa ut" ur en synkron TCP-kontakt med minimal ansträngning. Om du har idéer för att förbättra benchmarks, överväger jag gärna din pull-förfrågan. All kod med lanseringsinstruktioner och resultat publiceras i en separat förråd.

Källa: will.com

Lägg en kommentar