Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel

Në ekosistemin PHP aktualisht ekzistojnë dy lidhës për të punuar me serverin Tarantool - kjo është shtrirja zyrtare PECL tarantool/tarantool-php, shkruar në C, dhe tarantool-php/klient, shkruar në PHP. Unë jam autori i kësaj të fundit.

Në këtë artikull, unë do të doja të ndaj rezultatet e testimit të performancës së të dy bibliotekave dhe të tregoj se si, me ndryshime minimale në kod, mund të arrini një rritje të performancës 3-5 (në testet sintetike!).

Çfarë do të testojmë?

Ne do të testojmë ato të përmendura më lart sinkron lidhësit që funksionojnë në mënyrë asinkrone, paralele dhe asinkrone-paralele. 🙂 Ne gjithashtu nuk duam të prekim kodin e vetë lidhësve. Aktualisht ka disa shtesa në dispozicion për të arritur atë që dëshironi:

  • Swoole ― një kuadër asinkron me performancë të lartë për PHP. Përdoret nga gjigantë të tillë të internetit si Alibaba dhe Baidu. Që nga versioni 4.1.0 është shfaqur një metodë magjike SwooleRuntime::enableCoroutine(), i cili ju lejon të "konvertoni bibliotekat sinkrone të rrjetit PHP në ato asinkrone me një linjë kodi".
  • Async ishte deri vonë një zgjerim shumë premtues për punën asinkrone në PHP. Pse deri vonë? Fatkeqësisht, për një arsye të panjohur për mua, autori e fshiu depon dhe fati i ardhshëm i projektit është i paqartë. Do të më duhet ta përdor nga një nga pirunët. Ashtu si Swoole, kjo shtesë ju lejon të ndizni me lehtësi pantallonat tuaja me një lëvizje të dorës për të mundësuar asinkroninë duke zëvendësuar zbatimin standard të transmetimeve TCP dhe TLS me versionet e tyre asinkrone. Kjo bëhet përmes opsionit "async.tcp = 1".
  • Paralele ― një zgjerim mjaft i ri nga i mirënjohuri Joe Watkins, autor i bibliotekave të tilla si phpdbg, apcu, pthreads, pcov, uopz. Zgjatja ofron një API për multithreading në PHP dhe pozicionohet si një zëvendësim për threads. Një kufizim i rëndësishëm i bibliotekës është se ajo funksionon vetëm me versionin ZTS (Zend Thread Safe) të PHP.

Si do të testojmë?

Le të hapim një shembull Tarantool me regjistrimin e shkrimit përpara të çaktivizuar (wal_mode = asnjë) dhe rritja e tamponit të rrjetit (lexim përpara = 1 * 1024 * 1024). Opsioni i parë do të eliminojë punën me diskun, i dyti do të bëjë të mundur leximin e më shumë kërkesave nga buferi i sistemit operativ dhe në këtë mënyrë të minimizojë numrin e thirrjeve të sistemit.

Për standardet që punojnë me të dhënat (futja, fshirja, leximi, etj.), para fillimit të standardit, do të (ri)krijohet një hapësirë ​​memtx, në të cilën vlerat e indeksit parësor krijohen nga një gjenerator i vlerave të renditura të numrave të plotë (sekuencë).
Hapësira DDL duket si kjo:

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

Nëse është e nevojshme, përpara se të ekzekutohet standardi, hapësira mbushet me 10,000 tupa të formularit

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

Tuples arrihen duke përdorur një vlerë kyçe të rastësishme.

Vetë pikë referimi është një kërkesë e vetme për serverin, e cila ekzekutohet 10,000 herë (revolucione), të cilat, nga ana tjetër, ekzekutohen në përsëritje. Përsëritjet përsëriten derisa të gjitha devijimet kohore ndërmjet 5 përsëritjeve të jenë brenda një gabimi të pranueshëm prej 3%*. Pas kësaj, merret rezultati mesatar. Ka një pauzë 1 sekondë midis përsëritjeve për të parandaluar mbytjen e procesorit. Mbledhësi i plehrave të Luas çaktivizohet para çdo përsëritjeje dhe detyrohet të fillojë pasi të përfundojë. Procesi PHP nis vetëm me shtesat e nevojshme për standardin, me aktivizimin e bufferimit të daljes dhe të çaktivizuar grumbulluesin e mbeturinave.

* Numri i rrotullimeve, përsëritjeve dhe pragu i gabimit mund të ndryshohet në cilësimet e standardeve.

Mjedisi testues

Rezultatet e publikuara më poshtë u bënë në një MacBookPro (2015), sistem operativ - Fedora 30 (versioni i kernelit 5.3.8-200.fc30.x86_64). Tarantool u lançua në doker me parametrin "--network host".

Versionet e paketës:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, ndërtoni a872fc2f86
PHP: 7.3.11 (cli) (ndërtuar: 22 tetor 2019 08:11:04)
tarantool/klient: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch për 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
swoole e jashtme: 4.4.12
paralele jashtë: 1.1.3

* Fatkeqësisht, lidhësi zyrtar nuk funksionon me versionin PHP > 7.2. Për të përpiluar dhe ekzekutuar shtesën në PHP 7.3, më duhej të përdorja arnim.

Gjetjet

Modaliteti sinkron

Protokolli Tarantool përdor një format binar Paketa e Mesazheve për të serializuar mesazhe. Në lidhësin PECL, serializimi fshihet thellë në thellësi të bibliotekës dhe ndikon në procesin e kodimit nga kodi i tokës së përdoruesit nuk duket e mundur. Një lidhës i pastër PHP, përkundrazi, ofron mundësinë për të personalizuar procesin e kodimit duke zgjeruar koduesin standard ose duke përdorur zbatimin tuaj. Ekzistojnë dy kodues të disponueshëm jashtë kutisë, njëri bazohet në msgpack/msgpack-php (zgjatja zyrtare e MessagePack PECL), tjetra është aktive rybakit/msgpack (në PHP të pastër).

Përpara se të krahasojmë lidhësit, ne do të masim performancën e koduesve të MessagePack për lidhësin PHP dhe në testet e mëtejshme do të përdorim atë që tregon rezultatin më të mirë:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Megjithëse versioni PHP (Pure) është inferior ndaj shtrirjes PECL në shpejtësi, në projektet reale unë do të rekomandoja përsëri përdorimin e tij rybakit/msgpack, sepse në shtesën zyrtare të MessagePack specifikimi i formatit zbatohet vetëm pjesërisht (për shembull, nuk ka mbështetje për llojet e personalizuara të të dhënave, pa të cilat nuk do të mund të përdorni Decimal - një lloj i ri i të dhënave i prezantuar në Tarantool 2.3) dhe ka një numri i të tjerëve problem (përfshirë çështjet e përputhshmërisë me PHP 7.4). Epo, në përgjithësi, projekti duket i braktisur.

Pra, le të matim performancën e lidhësve në modalitetin sinkron:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Siç mund të shihet nga grafiku, lidhësi PECL (Tarantool) tregon performancë më të mirë në krahasim me lidhësin PHP (Klient). Por kjo nuk është për t'u habitur, duke qenë se kjo e fundit, përveçse zbatohet në një gjuhë më të ngadaltë, në fakt bën më shumë punë: me çdo thirrje krijohet një objekt i ri. Kërkesa и Përgjigje (në rastin e Select - gjithashtu kriteret, dhe në rastin e Përditësimit/Përditësimit ― operacionet), entitete të veçanta Lidhje, Paketues и mbajtës ata gjithashtu shtojnë shpenzimet e përgjithshme. Natyrisht, fleksibiliteti ka një çmim. Sidoqoftë, në përgjithësi, përkthyesi PHP tregon performancë të mirë, megjithëse ka një ndryshim, ai është i parëndësishëm dhe, ndoshta, do të jetë edhe më i vogël kur përdorni ngarkimin paraprak në PHP 7.4, për të mos përmendur JIT në PHP 8.

Le të vazhdojmë. Tarantool 2.0 shtoi mbështetje për SQL. Le të përpiqemi të kryejmë operacionet Select, Insert, Update dhe Delete duke përdorur protokollin SQL dhe të krahasojmë rezultatet me ekuivalentët noSQL (binare):

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Rezultatet e SQL nuk janë shumë mbresëlënëse (më lejoni t'ju kujtoj se ne jemi ende duke testuar modalitetin sinkron). Sidoqoftë, nuk do të mërzitesha për këtë para kohe; mbështetja SQL është ende në zhvillim aktiv (relativisht kohët e fundit, për shembull, u shtua mbështetje deklarata të përgatitura) dhe, duke gjykuar nga lista Çështjet, motori SQL do t'i nënshtrohet një numri optimizimesh në të ardhmen.

Asink

Epo, tani le të shohim se si zgjatja Async mund të na ndihmojë të përmirësojmë rezultatet e mësipërme. Për të shkruar programe asinkrone, zgjerimi ofron një API të bazuar në korutina, të cilat ne do të përdorim. Ne zbulojmë në mënyrë empirike se numri optimal i korutinave për mjedisin tonë është 25:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
"Përhapni" 10,000 operacione në 25 korutina dhe shikoni se çfarë ndodh:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Numri i operacioneve në sekondë u rrit me më shumë se 3 herë për tarantool-php/klient!

Mjerisht, lidhësi PECL nuk filloi me ext-async.

Po në lidhje me SQL?

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Siç mund ta shihni, në modalitetin asinkron, ndryshimi midis protokollit binar dhe SQL u bë brenda kufirit të gabimit.

Swoole

Përsëri zbulojmë numrin optimal të korutinave, këtë herë për Swoole:
Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Le të ndalemi në 25. Le të përsërisim të njëjtin truk si me shtesën Async - shpërndani 10,000 operacione midis 25 korutinave. Përveç kësaj, ne do të shtojmë një test tjetër në të cilin do ta ndajmë të gjithë punën në 2 dy procese (d.m.th., secili proces do të kryejë 5,000 operacione në 25 korutina). Proceset do të krijohen duke përdorur SwooleProcess.

Rezultatet:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Swole tregon një rezultat pak më të ulët në krahasim me Async kur ekzekutohet në një proces, por me 2 procese fotografia ndryshon në mënyrë dramatike (numri 2 nuk u zgjodh rastësisht; në makinën time ishin 2 procese që treguan rezultatin më të mirë).

Nga rruga, zgjerimi Async ka gjithashtu një API për të punuar me procese, por atje nuk vura re ndonjë ndryshim nga ekzekutimi i standardeve në një ose më shumë procese (është e mundur që të ngatërrova diku).

SQL kundrejt protokollit binar:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Ashtu si me Async, ndryshimi midis operacioneve binare dhe SQL eliminohet në modalitetin asinkron.

Paralele

Meqenëse shtrirja Parallel nuk ka të bëjë me korutinat, por me fijet, le të matim numrin optimal të fijeve paralele:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Është 16 në makinën time. Le të ekzekutojmë standardet e lidhësit në 16 fije paralele:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Siç mund ta shihni, rezultati është edhe më i mirë se sa me shtesat asinkrone (duke mos llogaritur Swoole që funksionon në 2 procese). Vini re se për lidhësin PECL, operacionet Update dhe Upsert janë bosh. Kjo për faktin se këto operacione dështuan me një gabim - nuk e di nëse ishte faji i ext-parallel, ext-tarantool, apo të dyja.

Tani le të krahasojmë performancën SQL:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel
Vini re ngjashmërinë me grafikun për lidhësit që funksionojnë në mënyrë sinkrone?

Së bashku

Dhe së fundi, le të përmbledhim të gjitha rezultatet në një grafik për të parë pamjen e përgjithshme për shtesat e testuara. Le të shtojmë vetëm një test të ri në grafik, të cilin nuk e kemi bërë ende - le të ekzekutojmë paralelisht korutinat Async duke përdorur Parallel*. Ideja e integrimit të shtesave të mësipërme është tashmë u diskutua autorë, por nuk u arrit konsensus, do të duhet ta bëni vetë.

* Nuk ishte e mundur të hapeshin korutinat Swoole me Parallel; duket se këto shtesa janë të papajtueshme.

Pra, rezultatet përfundimtare:

Përshpejtimi i lidhësve PHP për Tarantool duke përdorur Async, Swoole dhe Parallel

Në vend të një përfundimi

Sipas mendimit tim, rezultatet doli të ishin mjaft të denja, dhe për disa arsye jam i sigurt se ky nuk është kufiri! Nëse ju duhet ta vendosni këtë në një projekt të vërtetë vetëm për veten tuaj, unë do të them vetëm se për mua ishte një eksperiment interesant që ju lejon të vlerësoni se sa mund të "shtrydhni" nga një lidhës sinkron TCP me përpjekje minimale. Nëse keni ide për përmirësimin e standardeve, do të jem i lumtur të shqyrtoj kërkesën tuaj për tërheqje. I gjithë kodi me udhëzimet dhe rezultatet e nisjes publikohet në një të veçantë depove.

Burimi: www.habr.com

Shto një koment