
PHP ökosüsteemis on hetkel kaks konnektorit Tarantooli serveriga töötamiseks – ametlik PECL laiendus , mis on kirjutatud C-s, ja , mis on kirjutatud PHP-s. Mina olen selle viimase autor.
Selles artiklis soovin jagada tulemusi mõlema teegi jõudluse testimisest ja näidata, kuidas minimaalsete koodimuudatustega saab saavutada 3-5% jõudluse kasvu (sünthetikates testides!).
Mida me testime?
Testime ülalmainitud sünkrone konektoreid, käivitatuna asünkroonselt, paralleelselt ja asünkroonselt-paralleelselt. 🙂 Samuti ei soovi me puutuda konnektorite enda koodi. Hetkel on saadaval mitmeid laiendusi, mis aitavad soovitud eesmärki saavutada:
- ― kõrge jõudlusega asünkroonne raamistik PHP jaoks. Seda kasutavad sellised internetigigandid nagu Alibaba ja Baidu. Versioonist 4.1.0 on ilmunud maagiline meetod SwooleRuntime::enableCoroutine(), mis võimaldab "ühe koodireaga muuta PHP sünkrone võrgu raamatukogud asünkroonseteks".
- Async ― kuni hiljuti oli see üsna lootustandev laiendus asünkroonses töötamises PHP-s. Miks kuni hiljuti? Kahjuks, teadmata põhjusel, on autor kustutanud repositooriumi ja projekti tulevik on ebaselge. Peame kasutama haru. Nagu Swoole, võimaldab see laiendus hõlpsalt muuta tavalised TCP ja TLS voogud nende asünkroonseteks versioonideks. See toimub valiku kaudu "async.tcp = 1«.
- ― üsna uus laiendus tuntud Joe Watkinselt, kes on selliste raamatukogude autor nagu phpdbg, apcu, pthreads, pcov, uopz. See laiendus pakub API-d mitme lõimega töötamiseks PHP-s ja positsioneeritakse kui pthreadsi asendaja. Oluline piirang raamatukogus on see, et see töötab ainult ZTS (Zend Thread Safe) versiooniga PHP.
Kuidas me testime?
Käivitage Tarantooli eksemplar, millel on välja lülitatud ennetavate kirjutiste logi (wal_mode = none) ja suurendatud võrgupuhvri (readahead = 1 * 1024 * 1024). Esimene valik välistab töö koos kettaga, teine — võimaldab lugeda rohkem päringuid operatsioonisüsteemi puhvrisse ja seeläbi minimeerida süsteemikõnesid.
Benchmarkide jaoks, mis töötavad andmete (sisestamine, kustutamine, lugemine jne) kallal, luuakse enne testimise algust memtx-ruum, kus primaarindeksi väärtused genereeritakse järjestatud täisarvude genereerija abil (sequence).
DDL ruum näeb välja järgmiselt:
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}})Vajadusel täidetakse ruum enne testimise käivitamist 10,000 kursoriga kujul
{id, "tuplе_"}Kursoritele pääseb ligi juhusliku võtme väärtuse kaudu.
Ise testimine esindab ühte päringut serverisse, mis täidetakse 10,000 korda (revolutsioonid), mis omakorda täidetakse iteratsioonides. Iteratsioonid korduvad, kuni kõik ajavahemikud 5 iteratsiooni vahel jäävad lubatud 3%* veapiiri sisse. Pärast seda võetakse keskmine tulemus. Iteratsioonide vahel on 1-sekundiline paus, et vältida protsessori throttling'ut. Lua prügikogu kogumismoodul on iga iteratsiooni eel välja lülitatud ja sunnitakse käivituma pärast selle lõpetamist. PHP-protsess käivitatakse ainult nendega, mis on testimise jaoks vajalikud laiendused, koos väljundite puhversalviga ja ilma prügikogu kogumismoodulita.
* Revolutsioonide, iteratsioonide ja veapiiri väärtust saab muuta testimise seadetes.
Testikeskkond
Allpool avaldatud tulemused toimusid MacBookPro (2015) peal, operatsioonisüsteem — Fedora 30 (kerneli versioon 5.3.8-200.fc30.x86_64). Tarantool käivitus Dockeris parameetriga "--network host".
Pakettide versioonid:
Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (kokku pandud: 22. okt 2019 08:11:04)
tarantool/client: 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
ext-swoole: 4.4.12
ext-parallel: 1.1.3
* Kahjuks ametlik konektor ei toimi PHP versiooni > 7.2 puhul. PHP 7.3-l laienduse kompileerimiseks ja käivitamiseks tuli kasutada .
tulemused näitasid ainult nelja ebaolulise koodibloki kattuvust, mis olid tingitud POSIX ja ANSI C nõuetest.
Sünkroonsusrežiim
Tarantooli protokoll kasutab sõnumite serialiseerimiseks binaarset formaati. PECL konektoris on serialiseerimine peidetud sügavale raamatukogu sügavustesse ja userland-koodist ei saa kiirendust mõjutada PHP-l põhinev konnektor pakub vastupidiselt võimaluse kohandada kodeerimisprotsessi standardse kodeerija laiendamise või oma teostuse kasutamise kaudu. Karbis on saadaval kaks kodeerijat, millest üks põhineb (ametlik MessagePack PECL laiendus), teine - (puhta PHP-l põhinev).
Enne konnektorite võrdlemist mõõdame MessagePack kodeerijate jõudlust PHP konnektori jaoks ja edasistes testides kasutame seda, mis näitab parimat tulemust:

Kuigi PHP versioon (Pure) jääb kiiruselt PECL laiendusele alla, soovitaksin ma siiski reaalses projektis kasutada just , kuna ametlikus MessagePack laienduses on formaadi spetsifikatsioon teostatud vaid osaliselt (näiteks ei toeta see kasutaja defineeritud andmetüüpe, ilma milleta te ei saa kasutada Decimal - uut andmetüüpi, mis ilmus Tarantool 2.3) ja sellel on mitmeid teisi (sealhulgas ühilduvusprobleemid PHP 7.4-ga). Kokkuvõttes näeb projekt aga maha jäetud.
Nii et mõõdame konnektorite jõudlust sünkroonses režiimis:

Nagu jooniselt näha, näitab PECL konnektor (Tarantool) paremat jõudlust võrreldes PHP-l põhineva konnektoriga (Client). Kuid see ei ole üllatav, arvestades, et viimane, lisaks sellele, et on teostatud aeglasemas keeles, teeb põhimõtteliselt rohkem tööd: iga kutsumise ajal luuakse uus objekt Päring ja Vastus (Selecti puhul - veel ka Criteria, ja Update/Upsert puhul - Operations), eraldi entiteedid Ühendus, Packer ja Handler lisavad samuti üleliigset koormust. Kindlasti tuleb paindlikkuse eest maksta. Üldiselt näitab PHP tõlkija head jõudlust, kuigi vahe on olemas, on see ebaoluline ja võib olla veelgi väiksem, kui kasutada preloading PHP 7.4-s, rääkimata JIT-st PHP 8-s.
Liigume edasi. Tarantool 2.0-s lisandus SQL toetus. Proovime teostada Select, Insert, Update ja Delete toimingud SQL-protokolli kasutades ning võrreldame tulemusi noSQL (binaarsete) ekvivalentidega:

SQL-i tulemused ei ole kuigi muljetavaldavad (meenutan, et me veel testime sünkroonset režiimi). Siiski, ma ei hakkaks selle pärast enne aega muretsema, SQL toetamine on endiselt aktiivses arenduses (relatiivselt hiljuti lisati näiteks ) ja, lähtudes nimekirjast , ootel on SQL mootoril veel mitmed optimeerimised.
Async
Noh, vaatame nüüd, kuidas Async laiendus saab aidata meil eelnevaid tulemusi parandada. Asünkroonsete programmeerimise jaoks pakub laiendus API kurooide põhjal (coroutines), mille kasutamisest me ka lähtume. Katsetamise käigus selgub, et meie keskkonna optimaalne kurooide arv on 25:

„Jaotame“ 10 000 operatsiooni 25 kurooidi vahel ja vaatame, mis saame:

Operatsioonide arv sekundis kasvas enam kui 3 korda !
Kahjuks ei käivitunud PECL konnektor ega ext-async.
Aga mis SQL-iga?

Kuidas näha, et asünkroonses režiimis on erinevus binaarprotokolli ja SQL-i vahel jääb mõõtmisvea piiresse.
Swoole
Uurime uuesti optimaalset kurooide arvu, seekord juba Swoole'i jaoks:

Peatume 25 peal. Korrame sama trikki nagu Async laiendusega – jagame 10 000 operatsiooni 25 kurooidi vahel. Lisaks sellele lisame veel ühe testi, kus jagame kogu töö kaheks protsessiks (st iga protsess teeb 5 000 operatsiooni 25 kurooidi vahel). Protsessid luuakse läbi SwooleProcess.
Tulemused:

Swoole näitab veidi madalamat tulemust võrreldes Asynciga ühe protsessi käivitamisel, kuid kahe protsessiga muutub olukord kardinaalselt (arv 2 ei ole juhuslik, mu masinas näitasid just 2 protsessi parimat tulemust).
Muide, Async laiendusel on ka API protsessidega töötamiseks, kuid ma ei märganud seal mingit erinevust ühe või mitme protsessi käivitamisel (pole välistatud, et tegin kuskil vea).
SQL vs binaarprotokoll:

Nagu Asynciga, nii ka asünkroonses režiimis binaarne ja SQL-operatsioonide vahel ei ole mingit erinevust.
Parallel
Kuna Parallel laiendus ei käsitle kurooide, vaid lõime, mõõdame optimaalse paralleelsete lõimede arvu:

See on 16 mu masinas. Käivitame konnektorite bänshmarkid 16 paralleelse lõime peal:

Nagu näete, on tulemus isegi parem kui asünkroonsete laienduste puhul (välja arvatud Swoole kahe protsessiga). Pange tähele, et PECL konnektori puhul on Update ja Upsert operatsioonide kohal tühjus. See on seotud asjaoluga, et need operatsioonid kukkusid veaga välja – ei oska öelda, kas see oli ext-paralleli, ext-tarantooli või nende mõlema süü.
Nüüd võrrelge SQL jõudlust:

Kas olete tähele pannud sarnasust graafikuga konnektorite jaoks, mis on käivitatud sünkroonselt?
Kokkuvõttes
Ja lõpuks viime kõik tulemused üheks graafikuks, et näha üldpilti testitud laiendite kohta. Lisame graafikule vaid ühe uue testi, mida me veel teinud ei ole - käivitame Async koorutid on paralleelselt Parallel* abil. Idea sellest, kuidas käivitada eelpoolmainitud laiendeid, autorite poolt, kuid konsensust pole saavutatud, tuleb seda teha ise.
* Swoole koorutite käivitamine paralleelselt ei õnnestunud, tundub, et need laiendid ei ole ühilduvad.
Nii et lõppkokkuvõtte tulemused:

Lõpetuseks
Minu arvates olid tulemused üsna muljetavaldavad ja mul on mingi põhjus kindel olla, et see ei ole veel piir! Kas see on teile reaalses projektis vajalik, otsustate täielikult teie, vaid ütlen, et minu jaoks oli see huvitav katse, mis võimaldas hinnata, kui palju saab «välja pigistada» sünkroonsest TCP-ühendist minimaalsete pingutustega. Kui teil on ideid tulemusanalüütika parandamiseks - vaataksin rõõmuga teie pull-requesti. Kogu kood koos käivitamisjuhiste ja tulemustega on avaldatud eraldi .
Allikas: habr.com
