
PHP ekosistēmā šobrīd pastāv divi savienotāji, lai strādātu ar Tarantool serveri – oficiālā PECL paplašinājuma , kas rakstīts C, un , kas rakstīts PHP. Esmu pēdējā autors.
Šajā rakstā es vēlētos dalīties ar testēšanas rezultātiem, izmantojot abas bibliotēkas, un parādīt, kā ar minimālām izmaiņām kodā var sasniegt 3-5% veiktspējas pieaugumu (sintētiskajos testos!).
Ko testēsim?
Testēsim iepriekš minētos sinerģiskos savienotājus, palaistus asinhroni, paralēli un asinhroni-paralēli. 🙂 Tāpat mēs nevēlamies mainīt pašu savienotāju kodu. Šobrīd ir pieejami vairāki paplašinājumi, kas ļauj sasniegt vēlamo:
- ― augstas veiktspējas asinhronais ietvars PHP. To izmanto tādi interneta milži kā Alibaba un Baidu. Sākot ar versiju 4.1.0, parādījās burvju metode SwooleRuntime::enableCoroutine(), kas ļauj "ar vienu koda rindu pārveidot sinhronās tīkla bibliotēkas PHP par asinhronām".
- Async ― līdz nesenam laikam bija diezgan perspektīvs paplašinājums asinhronai darbībai PHP. Kāpēc līdz nesenam? Diemžēl, nezināmu iemeslu dēļ autors izdzēsa repozitoriju, un turpmākā projekta liktenis ir neskaidrs. Atliek izmantot no forkiem. Tāpat kā Swoole, šis paplašinājums ļauj viegli pārvērst sinhronitāti asinhronitātē, nomainot standarta TCP un TLS plūsmu uz to asinhronām versijām. To var izdarīt ar iespēju "async.tcp = 1«.
- ― salīdzinoši jauns paplašinājums no vispārzināmā Joe Watkins, kura radītie ir tādas bibliotēkas kā phpdbg, apcu, pthreads, pcov, uopz. Paplašinājums piedāvā API daudzpavedienu darbam PHP un pozicionējas kā pthreads aizvietotājs. Ierobežojošs faktors ir tas, ka bibliotēka darbojas tikai ar ZTS (Zend Thread Safe) PHP versiju.
Kā testēsim?
Uzpaliksim Tarantool instanci ar izslēgtu iepriekšējās rakstīšanas žurnālu (wal_mode = none) un palielinātu tīkla buferi (readahead = 1 * 1024 * 1024). Pirmais variants izslēgs darbu ar disku, otrais – ļaus izlasīt vairāk pieprasījumus no operētājsistēmas bufera, tādējādi minimizējot sistēmiskos izsaukumus.
Veicot pārbaudes, kas darbojas ar datiem (ievietošana, dzēšana, lasīšana utt.), pirms pārbaudes sākšanas tiks (pā)veidots memtx-krājums, kurā primārā indeksa vērtības tiek ģenerētas ar sakārtotu veselās skaitļu vērtību ģeneratoru (secība).
DDL krājums izskatās šādi:
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}})Ja nepieciešams, pirms pārbaudes uzsākšanas krājums tiek aizpildīts ar 10,000 krotēm formātā
{id, "tuplе_"}Piekļuve krotēm tiek veikta pēc nejauša atslēgas vērtības.
Pati pārbaude ir vienots pieprasījums uz serveri, kas tiek veikts 10,000 reižu (revolūcijas), kuras, savukārt, tiek izpildītas iterācijās. Iterācijas tiek atkārtotas, līdz visu laiku novirzes starp 5 iterācijām atrodas pieļaujamā kļūdas robežā 3 %*. Pēc tam tiek ņemts vidējais rezultāts. Starp iterācijām ir viena sekunde pārtraukums, lai nepieļautu procesora pārlieku noslodzi. Lua atkritumu savācējs tiek izslēgts pirms katras iterācijas un piespiedu kārtā aktivizēts pēc tās pabeigšanas. PHP process tiek uzsākts tikai ar nepieciešamajiem paplašinājumiem pārbaudei, izslēdzot atkritumu savācēju un ieslēdzot izejas buferizāciju.
* Revolūciju, iterāciju skaitu un kļūdas robežu var mainīt pārbaudes iestatījumos.
Testēšanas vide
Zemāk publicētie rezultāti tika iegūti uz MacBookPro (2015), operētājsistēma — Fedora 30 (kodola versija 5.3.8-200.fc30.x86_64). Tarantool tika uzsākts Docker, izmantojot parametrus "--network host".
Pakalpojumu versijas:
Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, build a872fc2f86
PHP: 7.3.11 (cli) (būvēts: 22. oktobris 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ labojumam 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallel: 1.1.3
* Diemžēl oficiālais savienotājs nesadarbojas ar PHP versiju > 7.2. Lai būtu iespējams kompilēt un uzsākt paplašinājumu PHP 7.3, bija jāizmanto .
Rezultāti
Sinhronais režīms
Tarantool protokols izmanto bināro formātu ziņu serializēšanai. PECL savienotājā serializācija ir dziļi noslēpta bibliotēkas iekšienē, un ietekmēt kodēšanas procesu no userland koda . PHP tīkla savienotājs, savukārt, sniedz iespēju pielāgot kodēšanas procesu, izmantojot standarta kodētāju paplašinājumu vai savas implementācijas izmantošanu. No kastes ir pieejami divi kodētāji, viens balstīts uz (oficiālais MessagePack PECL paplašinājums), otrs — uz (tīra PHP versija).
Pirms salīdzināšanas savienotājiem, izmērām MessagePack kodētāju veiktspēju PHP savienotājam, un tālākajos testos izmantosim to, kurš parādīs labāko rezultātu:

Lai gan PHP versija (Pure) ir lēnāka nekā PECL paplašinājums, es joprojām ieteiktu izmantot tieši , jo oficiālajā MessagePack paplašinājumā formāta specifikācija ir īstenota tikai daļēji (piemēram, nav atbalsta pielāgotajiem datu tipiem, bez kuriem jūs nespēsiet izmantot Decimal — jauno datu tipu, kas tika ieviests Tarantool 2.3) un tas satur vairākas citas (ieskaitot saderības problēmas ar PHP 7.4). Un vispār, projekts izskatās pamests.
Tātad, izmērīsim savienotāju veiktspēju sinhronajā režīmā:

Kā redzams diagrammā, PECL (Tarantool) savienotājs rāda labāku veiktspēju salīdzinājumā ar PHP (Client) savienotāju. Bet tas nav pārsteidzoši, ņemot vērā, ka pēdējais, ne tikai tiek īstenots lēnākā valodā, bet faktiski veic vairāk darba: katrā izsaukumā tiek izveidots jauns objekts Pieprasījums un Response (izvēles gadījumā — vēl arī Criteria, bet atjaunināšanas/iepriekšējās ievietošanas gadījumā — Operations), atsevišķām entītēm Connection, Packer un Handler tāpat pievieno papildus slodzi. Acīmredzami, ka par elastību nākas maksāt. Tomēr kopumā PHP interpretators rāda labu veiktspēju, lai gan atšķirības pastāv, tās nav nozīmīgas un, iespējams, būs vēl mazākas, izmantojot priekšrocības ar PHP 7.4, nerunājot par JIT PHP 8.
Virzāmies tālāk. Tarantool 2.0 ieguva SQL atbalstu. Mēģināsim izpildīt Select, Insert, Update un Delete operācijas, izmantojot SQL protokolu, un salīdzināsim rezultātus ar noSQL (binārajiem) ekvivalentiem:

SQL rezultāti nav īpaši iespaidīgi (atgādinu, ka mēs joprojām testējam sinhrono režīmu). Tomēr es negribētu par to satraukties pirms laika, SQL atbalsts joprojām ir aktīvā izstrādē (salīdzinoši nesen tika pievienots atbalsts ), un, spriežot pēc saraksta , SQL dzinējs nākotnē gaida virkni optimizāciju.
Async
Nu, aplūkosim, kā paplašinājums Async var palīdzēt uzlabot iepriekšējos rezultātus. Lai rakstītu asinkronās programmas, paplašinājums nodrošina API, kas balstīts uz korutīnām (coroutines), un mēs to izmantosim. Eksperimentāli noskaidrojam, ka optimālais korutīnu skaits mūsu vidē ir 25:

«Izkliedējam» 10,000 operācijas starp 25 korutīnām un skatāmies, kas iznāk:

Operāciju skaits sekundē pieauga vairāk nekā trīs reizes priekš !
Diemžēl PECL savienotājs nepalaižas ar ext-async.
Bet kā ir ar SQL?

Kā redzat, asinkronajā režīmā atšķirība starp bināro protokolu un SQL ir pieļaujamās kļūdas robežās.
Swoole
Atkal noskaidrojam optimālo korutīnu skaitu, tagad jau Swoole:

Apstāsimies pie 25. Atkārtojam to pašu triku, ko ar paplašinājumu Async ― izkaisām 10,000 operācijas starp 25 korutīnām. Papildus tam pievienosim vēl vienu testu, kurā visu darbu sadalām uz 2 procesiem (proti, katrs process veiks 5,000 operācijas 25 korutīnās). Procesi tiks izveidoti, izmantojot SwooleProcess.
Rezultāti:

Swoole rāda nedaudz zemāku rezultātu salīdzinājumā ar Async, ja to palaiž vienā procesā, bet ar 2 procesiem aina mainās kardināli (skaitlis 2 nav izvēlēts nejauši, manā mašīnā tieši 2 procesi parādīja labāko rezultātu).
P.S. Paplašinājumā Async ir arī API darbam ar procesiem, taču tur es nepamanīju nekādu atšķirību no benchmarku palaišanas vienā vai vairākos procesos (nav izslēgts, ka es kaut kur kļūdījos).
SQL pret bināro protokolu:

Tāpat kā ar Async, atšķirība starp binārajām un SQL operācijām samazinās asinkronajā režīmā.
Parallel
Tā kā paplašinājums Parallel neattiecas uz korutīnām, bet uz pavedieniem, izmērīsim optimālo paralēlo pavedienu skaitu:

Tas ir 16 manā mašīnā. Palaižam savienotāju benchmarkus uz 16 paralēliem pavedieniem:

Kā redzat, rezultāts ir pat labāks nekā ar asinkronajiem paplašinājumiem (izņemot Swoole, kas darbojas uz 2 procesiem). Pievērsiet uzmanību, ka PECL savienotājam vietās, kur bija Update un Upsert operācijas, ir tukši. Tas saistīts ar to, ka šīs operācijas izgāzās ar kļūdu — nezinu, vai par to ir atbildīgs ext-parallel, ext-tarantool vai abi.
Tagad salīdzināsim SQL veiktspēju:

Pamanījāt līdzību ar grafiku savienotājiem, kas darbojas sinhroni?
Viss kopā
Un visbeidzot, apvienosim visus rezultātus vienā grafikā, lai redzētu kopējo ainu par testētajiem paplašinājumiem. Grafikā pievienosim tikai vienu jaunu testu, kuru vēl neesam veikuši — palaidīsim korutīnas Async paralēli, izmantojot Parallel*. Ideja par iepriekšminēto paplašinājumu integrāciju jau autoru vidū, taču konsenss netika panākts, tāpēc nāksies to izdarīt pašiem.
* Palaist Swoole korutīnas ar Parallel neizdevās, šķiet, ka šie paplašinājumi nav savietojami.
Tādējādi, galīgie rezultāti:

Noslēgumā
Manuprāt, rezultāti ir diezgan cienījami, un kādā veidā esmu pārliecināts, ka tas vēl nav gals! Vai tas jums ir nepieciešams reālajā projektā, izlemj tikai jums pašiem, varu teikt tikai to, ka man tas bija interesants eksperiments, kas ļāva novērtēt, cik daudz var „izspiest” no sinhronā TCP savienotāja ar minimālām pūlēm. Ja jums ir idejas par benchmark uzlabošanu, es labprāt izskatīšu jūsu pieprasījumu. Visi kods ar palaišanas instrukcijām un rezultātiem ir publicēts atsevišķā .
Avots: habr.com
