We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel

In het PHP-ecosysteem zijn er momenteel twee connectors beschikbaar voor het werken met de Tarantool-server: de officiële PECL-extensie tarantool/tarantool-php, geschreven in C, en tarantool-php/client, geschreven in PHP. Ik ben de auteur van de laatste.

In dit artikel wil ik de resultaten delen van de prestatie-testen van beide bibliotheken en laten zien hoe je met minimale codewijzigingen een prestatieverbetering van 3-5% kunt bereiken (bij synthetische tests!).

Wat gaan we testen?

We gaan de hierboven genoemde synchrone connectors testen, uitgevoerd asynchroon, parallel en asynchroon-parallel. 🙂 Ook willen we de code van de connectors zelf niet aanpassen. Op dit moment zijn er verschillende extensies beschikbaar die het gewenste resultaat kunnen leveren:

  • Swoole ― een hoogwaardige asynchrone framework voor PHP. Het wordt gebruikt door internetgiganten zoals Alibaba en Baidu. Sinds versie 4.1.0 is er een magische methode toegevoegd, SwooleRuntime::enableCoroutine(), waarmee "met één regel code synchrone netwerken PHP-bibliotheken in asynchrone kunnen worden omgezet."
  • Async ― tot voor kort een veelbelovende extensie voor asynchrone werking in PHP. Waarom tot voor kort? Helaas heeft de auteur om mij onbekende redenen de repository verwijderd en is de toekomst van het project onzeker. We zullen gebruik moeten maken van één van de forks. Net als Swoole stelt deze extensie je in staat om eenvoudig asynchroniciteit in te schakelen door de standaardimplementatie van TCP en TLS-stromen te vervangen door hun asynchrone versies. Dit wordt gedaan via de optie "async.tcp = 1«.
  • Parallel ― een vrij nieuwe extensie van de bekende Joe Watkins, de auteur van bibliotheken zoals phpdbg, apcu, pthreads, pcov, uopz. Deze extensie biedt een API voor multithreading in PHP en wordt gepositioneerd als een vervanging voor pthreads. Een belangrijk nadeel van de bibliotheek is dat deze alleen werkt met de ZTS (Zend Thread Safe) versie van PHP.

Hoe gaan we testen?

We starten een instantie van Tarantool met de write-ahead-log uitgeschakeld (wal_mode = none) en een vergroot netwerkbuffer (readahead = 1 * 1024 * 1024). De eerste optie sluit schijfactiviteit uit, de tweede geeft de mogelijkheid om meer verzoeken uit de buffer van het besturingssysteem te lezen en vermindert daarmee het aantal systeemaanroepen.

Voor benchmarks die met gegevens werken (invoegen, verwijderen, lezen, enz.) zal vóór de start van de benchmark een memtx-ruimte worden (her)gecreëerd, waarin de waarden van de primaire index worden gegenereerd door een generator van geordende gehele getallen (sequence).
DDL van de ruimte ziet er als volgt uit:

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

Desgewenst, vóór de uitvoering van de benchmark, wordt de ruimte gevuld met 10.000 tuple's van de vorm

{id, "tuple_"}

Toegang tot de tuple's is mogelijk via een willekeurige sleutelwaarde.

De benchmark zelf bestaat uit een enkele aanvraag aan de server die 10.000 keer (revoluties) wordt uitgevoerd, die op hun beurt in iteraties worden uitgevoerd. De iteraties worden herhaald totdat alle afwijkingen in de tijd tussen 5 iteraties binnen de toegestane foutmarge van 3% vallen. Daarna wordt het gemiddelde resultaat genomen. Tussen de iteraties is er een pauze van 1 seconde om te voorkomen dat de processor in throttling gaat. De Lua-garbage collector wordt vóór elke iteratie uitgeschakeld en gedwongen gestart na de voltooiing ervan. Het PHP-proces wordt alleen gestart met de voor de benchmark benodigde extensies, met ingeschakelde uitvoerbuffer en uitgeschakelde garbage collector.

* Het aantal revoluties, iteraties en de foutdrempel kan worden aangepast in de benchmarkinstellingen.

Testomgeving

De hieronder gepubliceerde resultaten zijn gemaakt op een MacBookPro (2015), besturingssysteem — Fedora 30 (kernelversie 5.3.8-200.fc30.x86_64). Tarantool werd uitgevoerd in Docker met de parameter "--network host".

Versies van de pakketten:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (gebouwd: 22 okt 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch voor 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallel: 1.1.3

* Helaas werkt de officiële connector niet met PHP-versie > 7.2. Om de extensie te compileren en uit te voeren op PHP 7.3 moest ik gebruikmaken van patch.

Resultaten

Synchrone modus

Het protocol van Tarantool gebruikt een binair formaat MessagePack voor het serialiseren van berichten. In de PECL-connector is serialisatie diep verborgen in de fundamenten van de bibliotheek en heeft geen invloed op het coderingsproces vanuit userland-code niet mogelijk.. De connector in schone PHP biedt daarentegen de mogelijkheid om het coderingproces aan te passen door het standaardcoderingstype uit te breiden of een eigen implementatie te gebruiken. Er zijn standaard twee coderingstypes beschikbaar, de ene is gebaseerd op msgpack/msgpack-php (officiële MessagePack PECL-extensie), de andere is gebaseerd op rybakit/msgpack (in schone PHP).

Voordat we de connectors vergelijken, laten we de prestaties van de MessagePack-coders voor de PHP-connector meten, en in de verdere tests zullen we die gebruiken die het beste resultaat oplevert:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Hoewel de PHP-versie (Pure) in snelheid inferieur is aan de PECL-extensie, zou ik in praktische projecten toch aanbevelen om juist rybakit/msgpack, omdat in de officiële MessagePack-extensie de specificatie van het formaat slechts gedeeltelijk is geïmplementeerd (bijvoorbeeld, er is geen ondersteuning voor aangepaste datatypes, zonder welke je de Decimal - een nieuw datatype geïntroduceerd in Tarantool 2.3 - niet kunt gebruiken) en een aantal andere problemen (waaronder compatibiliteitsproblemen met PHP 7.4). En over het algemeen lijkt het project verlaten.

Laten we dus de prestaties van de connectors in synchrone modus meten:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Zoals te zien is in de grafiek, biedt de PECL-connector (Tarantool) betere prestaties in vergelijking met de PHP-connector (Client). Maar dat is ook niet verwonderlijk, rekening houdend met het feit dat de laatstgenoemde naast het feit dat deze is geïmplementeerd in een langzamere taal, in wezen meer werk verricht: bij elke oproep wordt er een nieuw object aangemaakt Request en Response (in het geval van Select - ook nog eens Criteria, en in het geval van Update/Upsert - Operations), aparte entiteiten Verbinden, Packer en Handler voegen ook overhead toe. Het is duidelijk dat we moeten betalen voor flexibiliteit. Desalniettemin toont de PHP-interpreter over het geheel genomen goede prestaties, hoewel er een verschil is, dit is minimaal en zal mogelijk nog kleiner zijn met het gebruik van preloading in PHP 7.4, om nog maar te zwijgen over JIT in PHP 8.

Laten we verder gaan. In Tarantool 2.0 werd SQL-ondersteuning geïntroduceerd. Laten we proberen de operaties Select, Insert, Update en Delete uit te voeren met behulp van het SQL-protocol en de resultaten vergelijken met de noSQL (binaire) equivalenten:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
De SQL-resultaten zijn niet bijzonder indrukwekkend (ik herinner eraan dat we nog steeds de synchrone modus testen). Maar ik zou me daar eerder niet te druk over maken, de ondersteuning van SQL is nog steeds actief in ontwikkeling (relatief recent is bijvoorbeeld de ondersteuning voor prepared statements) toegevoegd, en gezien de lijst issues, staat de SQL-engine nog een aantal optimalisaties te wachten.

Asynchroon

Laten we nu eens kijken hoe de Async-extensie ons kan helpen om de bovenstaande resultaten te verbeteren. Voor het schrijven van asynchrone programma's biedt de extensie een API op basis van coroutines, die we zullen gebruiken. Door experimenteel vast te stellen, komen we erachter dat het optimale aantal coroutines voor onze omgeving gelijk is aan 25:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
We 'verdeling' 10.000 operaties over 25 coroutines en kijken wat we hebben gekregen:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Het aantal operaties per seconde is meer dan verdrievoudigd voor tarantool-php/client!

Helaas, de PECL-connector startte niet met ext-async.

En SQL?

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Zoals je ziet, is het verschil tussen het binaire protocol en SQL in asynchrone modus binnen de foutmarge gekomen.

Swoole

Laten we opnieuw het optimale aantal coroutines vaststellen, dit keer voor Swoole:
We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
We blijven bij 25. We herhalen dezelfde truc als met de Async-extensie - we verdelen 10.000 operaties over 25 coroutines. Bovendien voegen we een test toe waarbij we al het werk verdelen over 2 processen (dat wil zeggen, elk proces zal 5.000 operaties uitvoeren in 25 coroutines). Processen worden aangemaakt met behulp van SwooleProcess.

Resultaten:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Swole laat een iets lager resultaat zien in vergelijking met Async wanneer uitgevoerd in één proces, maar met 2 processen verandert het beeld dramatisch (het nummer 2 is niet toevallig gekozen, op mijn machine lieten juist 2 processen het beste resultaat zien).

Overigens, de Async-extensie heeft ook een API voor het werken met processen, maar daar heb ik geen verschil opgemerkt tussen het uitvoeren van benchmarks in één of meerdere processen (het is mogelijk dat ik ergens een fout heb gemaakt).

SQL vs binaire protocol:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Net als bij Async, wordt het verschil tussen binaire en SQL-operaties in de asynchrone modus opgeheven.

Parallel

Aangezien de Parallel-extensie niet over coroutines gaat, maar over threads, meten we het optimale aantal parallelle threads:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Het is gelijk aan 16 op mijn machine. Laten we de benchmarks van de connectors uitvoeren op 16 parallelle threads:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Zoals je ziet, is het resultaat zelfs beter dan met de asynchrone extensies (behalve Swoole die op 2 processen draait). Merk op dat er voor de PECL-connector op de Update en Upsert-operaties niets is ingevuld. Dit heeft te maken met het feit dat deze operaties met een fout zijn beëindigd - ik weet niet of het de schuld van ext-parallel, ext-tarantool of beide is.

Laten we nu de prestaties van SQL vergelijken:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel
Zie je de overeenkomst met de grafiek voor de connectors die synchroon zijn uitgevoerd?

Alles samen

En tot slot, laten we alle resultaten samenvoegen in één grafiek om een algemeen beeld te krijgen van de geteste extensies. We voegen slechts één nieuwe test toe die we nog niet hebben gedaan - we zullen Async coroutines parallel uitvoeren met behulp van Parallel*. Het idee om de bovengenoemde extensies te integreren is reeds besproken door de auteurs, maar er is geen consensus bereikt, dus zullen we het zelf moeten doen.

* Het is niet gelukt om Swoole coroutines met Parallel uit te voeren; het lijkt erop dat deze extensies niet compatibel zijn.

Dus, de finale resultaten:

We versnellen PHP-connectoren voor Tarantool met Async, Swoole en Parallel

Ter afsluiting

Ik vind de resultaten behoorlijk goed, en om de een of andere reden ben ik ervan overtuigd dat dit nog niet het eindpunt is! Of dit nuttig voor u is in een echt project, is volledig aan u zelf om te bepalen. Ik kan alleen zeggen dat het voor mij een interessante experiment was, waardoor ik kon inschatten hoeveel men kan 'halen' uit een synchroon TCP-verbinding met minimale inspanning. Als u ideeën heeft om de benchmarks te verbeteren, dan zie ik graag uw pull request tegemoet. Alle code met instructies voor uitvoering en resultaten is gepubliceerd in een aparte de repository.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster