Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel

In het PHP-ecosysteem zijn er momenteel twee connectoren voor het werken met de Tarantool-server - dit is de officiële PECL-extensie tarantool/tarantool-php, geschreven in C, en tarantool-php/client, geschreven in PHP. Van dat laatste ben ik de auteur.

In dit artikel wil ik de resultaten van de prestatietests van beide bibliotheken delen en laten zien hoe je met minimale wijzigingen in de code een prestatieverbetering van 3-5 kunt bereiken (op synthetische tests!).

Wat gaan we testen?

We zullen de hierboven genoemde testen synchroon connectoren die asynchroon, parallel en asynchroon parallel lopen. 🙂 We willen ook niet aan de code van de connectoren zelf komen. Er zijn momenteel verschillende extensies beschikbaar om te bereiken wat u wilt:

  • Swol – een krachtig asynchronisch raamwerk voor PHP. Gebruikt door internetgiganten als Alibaba en Baidu. Sinds versie 4.1.0 is er een magische methode verschenen SwoleRuntime::enableCoroutine(), waarmee u “synchrone PHP-netwerkbibliotheken met één regel code naar asynchrone kunt converteren.”
  • Async was tot voor kort een veelbelovende extensie voor asynchroon werken in PHP. Waarom tot voor kort? Helaas heeft de auteur om een ​​voor mij onbekende reden de repository verwijderd en is het toekomstige lot van het project onduidelijk. Ik zal het moeten gebruiken een van vorken. Net als Swole kun je met deze extensie eenvoudig je broek aanzetten met een beweging van de pols om asynchronie mogelijk te maken door de standaardimplementatie van TCP- en TLS-streams te vervangen door hun asynchrone versies. Dit gebeurt via de optie “async.tcp = 1".
  • Parallel – een vrij nieuwe extensie van de bekende Joe Watkins, auteur van bibliotheken als phpdbg, apcu, pthreads, pcov, uopz. De extensie biedt een API voor multithreading in PHP en is gepositioneerd als vervanging voor pthreads. Een belangrijke beperking van de bibliotheek is dat deze alleen werkt met de ZTS-versie (Zend Thread Safe) van PHP.

Hoe gaan we testen?

Laten we een Tarantool-instantie starten waarbij vooruitschrijven van logboekregistratie is uitgeschakeld (wal_mode = geen) en verhoogde netwerkbuffer (vooruitlezen = 1 * 1024 * 1024). De eerste optie elimineert het werk met de schijf, de tweede maakt het mogelijk om meer verzoeken uit de buffer van het besturingssysteem te lezen en daardoor het aantal systeemaanroepen te minimaliseren.

Voor benchmarks die werken met data (invoegen, verwijderen, lezen, etc.) wordt vóór het starten van de benchmark een memtx-ruimte (opnieuw) aangemaakt, waarin de primaire indexwaarden worden aangemaakt door een generator van geordende gehele waarden ​​(volgorde).
De ruimte DDL 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}})

Indien nodig wordt de ruimte, voordat de benchmark wordt uitgevoerd, gevuld met 10,000 tupels van het formulier

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

Tupels zijn toegankelijk via een willekeurige sleutelwaarde.

De benchmark zelf is een enkel verzoek aan de server, dat 10,000 keer (omwentelingen) wordt uitgevoerd, die op hun beurt in iteraties worden uitgevoerd. De iteraties worden herhaald totdat alle tijdafwijkingen tussen 5 iteraties binnen een aanvaardbare fout van 3%* liggen. Hierna wordt het gemiddelde resultaat genomen. Er is een pauze van 1 seconde tussen de iteraties om te voorkomen dat de processor gaat smoren. De garbage collector van Lua wordt vóór elke iteratie uitgeschakeld en wordt gedwongen te starten nadat deze is voltooid. Het PHP-proces wordt alleen gestart met de extensies die nodig zijn voor de benchmark, waarbij uitvoerbuffering is ingeschakeld en de garbage collector is uitgeschakeld.

* Het aantal omwentelingen, iteraties en foutdrempel kunnen worden gewijzigd in de benchmarkinstellingen.

Test omgeving

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

Pakketversies:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, bouw a872fc2f86
PHP: 7.3.11 (cli) (gebouwd: 22 oktober 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-asynchrone: 0.3.0-8c1da46
ext-swole: 4.4.12
ext-parallel: 1.1.3

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

Bevindingen

Synchrone modus

Het Tarantool-protocol gebruikt een binair formaat Berichtenpakket om berichten te serialiseren. In de PECL-connector is serialisatie diep in de diepte van de bibliotheek verborgen en beïnvloedt het coderingsproces van gebruikerscode lijkt niet mogelijk. Een pure PHP-connector biedt daarentegen de mogelijkheid om het coderingsproces aan te passen door de standaard encoder uit te breiden of door uw eigen implementatie te gebruiken. Er zijn standaard twee encoders beschikbaar, waarvan er één is gebaseerd op msgpack/msgpack-php (officiële MessagePack PECL-extensie), de andere is ingeschakeld rybakit/msgpack (in puur PHP).

Voordat we connectoren vergelijken, meten we de prestaties van MessagePack-encoders voor de PHP-connector en bij verdere tests zullen we degene gebruiken die het beste resultaat laat zien:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Hoewel de PHP-versie (Pure) qua snelheid inferieur is aan de PECL-extensie, zou ik in echte projecten toch aanraden deze te gebruiken rybakit/msgpack, omdat in de officiële MessagePack-extensie de formaatspecificatie slechts gedeeltelijk is geïmplementeerd (er is bijvoorbeeld geen ondersteuning voor aangepaste gegevenstypen, zonder welke u Decimal niet kunt gebruiken - een nieuw gegevenstype geïntroduceerd in Tarantool 2.3) en een aantal anderen problemen (inclusief compatibiliteitsproblemen met PHP 7.4). Over het algemeen ziet het project er verlaten uit.

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

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Zoals uit de grafiek blijkt, vertoont de PECL-connector (Tarantool) betere prestaties vergeleken met de PHP-connector (Client). Maar dit is niet verrassend, aangezien dit laatste, naast het feit dat het in een langzamere taal is geïmplementeerd, feitelijk meer werk doet: bij elke aanroep wordt een nieuw object gemaakt. Aanvraag и antwoord (in het geval van Selecteren - ook criteria, en in het geval van Update/Upsert ― Operations), afzonderlijke entiteiten Aansluiting, Packer и Handler ze voegen ook overhead toe. Het is duidelijk dat flexibiliteit een prijs heeft. Over het algemeen vertoont de PHP-interpreter echter goede prestaties, hoewel er een verschil is, het is onbeduidend en zal misschien zelfs nog minder zijn bij gebruik van preloading in PHP 7.4, om nog maar te zwijgen van JIT in PHP 8.

Laten we verder gaan. Tarantool 2.0 heeft ondersteuning voor SQL toegevoegd. Laten we proberen de bewerkingen Selecteren, Invoegen, Bijwerken en Verwijderen uit te voeren met behulp van het SQL-protocol en de resultaten vergelijken met de noSQL (binaire) equivalenten:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
De SQL-resultaten zijn niet erg indrukwekkend (ik wil u eraan herinneren dat we nog steeds de synchrone modus testen). Ik zou hier echter niet van tevoren boos over worden; SQL-ondersteuning is nog steeds in actieve ontwikkeling (relatief recent is er bijvoorbeeld ondersteuning toegevoegd voorbereide verklaringen) en, afgaande op de lijst problemen, zal de SQL-engine in de toekomst een aantal optimalisaties ondergaan.

asynchrone

Laten we nu eens kijken hoe de Async-extensie ons kan helpen de bovenstaande resultaten te verbeteren. Om asynchrone programma's te schrijven, biedt de extensie een API op basis van coroutines, die we zullen gebruiken. We komen er empirisch achter dat het optimale aantal coroutines voor onze omgeving 25 is:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
“Verspreid” 10,000 operaties over 25 coroutines en kijk wat er gebeurt:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Het aantal bewerkingen per seconde is ruim drie keer zo groot geworden tarantool-php/client!

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

Hoe zit het met SQL?

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Zoals u kunt zien, viel het verschil tussen het binaire protocol en SQL in de asynchrone modus binnen de foutmarge.

Swol

Opnieuw ontdekken we het optimale aantal coroutines, dit keer voor Swole:
Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Laten we stoppen bij 25. Laten we dezelfde truc herhalen als bij de Async-extensie: verdeel 10,000 bewerkingen over 25 coroutines. Daarnaast zullen we nog een test toevoegen waarin we al het werk in twee processen zullen verdelen (dat wil zeggen, elk proces zal 2 bewerkingen uitvoeren in 5,000 coroutines). Processen worden gemaakt met behulp van SwoleProces.

Resultaten:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Swole laat een iets lager resultaat zien vergeleken met Async wanneer het in één proces wordt uitgevoerd, maar met 2 processen verandert het beeld dramatisch (het getal 2 is niet toevallig gekozen; op mijn machine waren het 2 processen die het beste resultaat lieten zien).

Overigens heeft de Async-extensie ook een API om met processen te werken, maar daar merkte ik geen verschil met het draaien van benchmarks in een of meerdere processen (het kan zijn dat ik ergens een fout heb gemaakt).

SQL versus binair protocol:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Net als bij Async wordt het verschil tussen binaire en SQL-bewerkingen geëlimineerd in de asynchrone modus.

Parallel

Omdat de parallelle extensie niet over coroutines gaat, maar over threads, gaan we het optimale aantal parallelle threads meten:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Op mijn machine is het 16. Laten we connectorbenchmarks uitvoeren op 16 parallelle threads:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Zoals u kunt zien is het resultaat zelfs beter dan met asynchrone extensies (afgezien van Swole die op twee processen draait). Houd er rekening mee dat voor de PECL-connector de bewerkingen Update en Upsert leeg zijn. Dit komt door het feit dat deze bewerkingen mislukten vanwege een fout - ik weet niet of dit de schuld was van ext-parallel, ext-tarantool of beide.

Laten we nu de SQL-prestaties vergelijken:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel
Zie je de gelijkenis met de grafiek voor connectoren die synchroon lopen?

Samen

En laten we tot slot alle resultaten in één grafiek samenvatten om het totaalbeeld van de geteste extensies te zien. Laten we slechts één nieuwe test aan het diagram toevoegen, wat we nog niet hebben gedaan: laten we Async-coroutines parallel uitvoeren met behulp van Parallel*. Het idee om bovenstaande extensies te integreren is al besproken auteurs, maar er is geen consensus bereikt, u zult het zelf moeten doen.

* Het was niet mogelijk om Swole-coroutines met Parallel te starten; het lijkt erop dat deze extensies incompatibel zijn.

Dus de eindresultaten:

Versnellen van PHP-connectoren voor Tarantool met behulp van Async, Swoole en Parallel

In plaats Output

Naar mijn mening bleken de resultaten behoorlijk de moeite waard, en om de een of andere reden ben ik er zeker van dat dit niet de limiet is! Of je dit in een echt project alleen voor jezelf moet beslissen, ik kan alleen maar zeggen dat het voor mij een interessant experiment was waarmee je kunt evalueren hoeveel je met minimale inspanning uit een synchrone TCP-connector kunt "knijpen". Als u ideeën heeft om benchmarks te verbeteren, zal ik uw pull-verzoek graag in overweging nemen. Alle code met startinstructies en resultaten wordt afzonderlijk gepubliceerd opslagplaatsen.

Bron: www.habr.com

Voeg een reactie