Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel

A l'ecosistema PHP actualment hi ha dos connectors per treballar amb el servidor Tarantool: aquesta és l'extensió oficial PECL tarantool/tarantool-php, escrit en C, i tarantool-php/client, escrit en PHP. Soc l'autor d'aquest últim.

En aquest article, m'agradaria compartir els resultats de les proves de rendiment d'ambdues biblioteques i mostrar com, amb canvis mínims al codi, podeu aconseguir un augment de rendiment de 3 a 5 (en proves sintètiques!).

Què provarem?

Provarem els esmentats anteriorment sincrònic connectors que funcionen de manera asíncrona, en paral·lel i de manera asíncrona-paral·lela. 🙂 Tampoc volem tocar el codi dels mateixos connectors. Actualment hi ha diverses extensions disponibles per aconseguir el que voleu:

  • Swoole ― un marc asíncron d'alt rendiment per a PHP. Utilitzat per gegants d'Internet com Alibaba i Baidu. Des de la versió 4.1.0 ha aparegut un mètode màgic SwooleRuntime::enableCoroutine(), que us permet "convertir biblioteques de xarxa PHP síncrones en asíncrones amb una línia de codi".
  • Async era fins fa poc una extensió molt prometedora per al treball asíncron en PHP. Per què fins fa poc? Malauradament, per un motiu desconegut per a mi, l'autor va suprimir el repositori i el destí futur del projecte no està clar. L'hauré d'utilitzar per un de forquilles. Igual que Swoole, aquesta extensió us permet encendre fàcilment els pantalons amb un toc del canell per habilitar l'asincronia substituint la implementació estàndard dels fluxos TCP i TLS per les seves versions asíncrones. Això es fa mitjançant l'opció "async.tcp = 1".
  • Paral · lel ― una extensió força nova del conegut Joe Watkins, autor de biblioteques com phpdbg, apcu, pthreads, pcov, uopz. L'extensió proporciona una API per multithreading en PHP i es posiciona com a substitut dels pthreads. Una limitació important de la biblioteca és que només funciona amb la versió ZTS (Zend Thread Safe) de PHP.

Com farem la prova?

Llencem una instància de Tarantool amb el registre d'escriptura anticipada desactivat (wal_mode = cap) i augment del buffer de xarxa (cap de lectura = 1 * 1024 * 1024). La primera opció eliminarà el treball amb el disc, la segona permetrà llegir més peticions de la memòria intermèdia del sistema operatiu i, per tant, minimitzar el nombre de trucades al sistema.

Per als benchmarks que funcionen amb dades (inserció, supressió, lectura, etc.), abans d'iniciar el benchmark, es (re)crearà un espai memtx, en el qual els valors d'índex primaris són creats per un generador de valors enters ordenats ​(seqüència).
L'espai DDL té aquest aspecte:

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

Si cal, abans d'executar el benchmark, l'espai s'omple amb 10,000 tuples del formulari

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

S'accedeix a les tuples mitjançant un valor de clau aleatori.

El punt de referència en si és una sol·licitud única al servidor, que s'executa 10,000 vegades (revolucions), que, al seu torn, s'executen en iteracions. Les iteracions es repeteixen fins que totes les desviacions de temps entre 5 iteracions estiguin dins d'un error acceptable del 3%*. Després d'això, es pren el resultat mitjà. Hi ha una pausa d'1 segon entre iteracions per evitar que el processador s'acceleri. El col·lector d'escombraries de Lua es desactiva abans de cada iteració i es veu obligat a iniciar-se un cop finalitzada. El procés PHP només es llança amb les extensions necessàries per al benchmark, amb la memòria intermèdia de sortida activada i el col·lector d'escombraries desactivat.

* El nombre de revolucions, iteracions i el llindar d'error es poden canviar a la configuració de referència.

Entorn de prova

Els resultats publicats a continuació es van fer en un sistema operatiu MacBookPro (2015), Fedora 30 (versió del nucli 5.3.8-200.fc30.x86_64). Tarantool es va llançar a Docker amb el paràmetre "--network host".

Versions de paquets:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, compila a872fc2f86
PHP: 7.3.11 (cli) (construït: 22 d'octubre de 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ pegat per a 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-paral·lel: 1.1.3

* Malauradament, el connector oficial no funciona amb la versió PHP > 7.2. Per compilar i executar l'extensió a PHP 7.3, vaig haver d'utilitzar pegat.

Troballes

Mode síncron

El protocol Tarantool utilitza un format binari Paquet de missatges per serialitzar missatges. Al connector PECL, la serialització s'amaga profundament a les profunditats de la biblioteca i afecta el procés de codificació del codi de l'usuari. no sembla possible. Un connector PHP pur, per contra, ofereix la possibilitat de personalitzar el procés de codificació ampliant el codificador estàndard o utilitzant la vostra pròpia implementació. Hi ha dos codificadors disponibles fora de la caixa, un es basa msgpack/msgpack-php (extensió oficial MessagePack PECL), l'altre està activat rybakit/msgpack (en PHP pur).

Abans de comparar connectors, mesurarem el rendiment dels codificadors MessagePack per al connector PHP i en proves posteriors utilitzarem el que mostri el millor resultat:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Tot i que la versió PHP (Pure) és inferior a l'extensió PECL en velocitat, en projectes reals encara recomanaria utilitzar-la. rybakit/msgpack, perquè a l'extensió oficial MessagePack l'especificació del format només s'implementa parcialment (per exemple, no hi ha suport per a tipus de dades personalitzats, sense els quals no podreu utilitzar Decimal, un nou tipus de dades introduït a Tarantool 2.3) i té un nombre d'altres problemes (inclosos problemes de compatibilitat amb PHP 7.4). Bé, en general, el projecte sembla abandonat.

Per tant, mesurem el rendiment dels connectors en mode síncron:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Com es pot veure al gràfic, el connector PECL (Tarantool) mostra un millor rendiment en comparació amb el connector PHP (Client). Però això no és d'estranyar, atès que aquest últim, a més d'implementar-se en un llenguatge més lent, en realitat fa més feina: es crea un nou objecte amb cada trucada. Sol · licitar и resposta (en el cas de Select - també Criteris, i en el cas d'Actualitzar/Usert ― operacions), entitats separades Connexió, Packer и Handler també afegeixen sobrecàrrega. Evidentment, la flexibilitat té un preu. No obstant això, en general, l'intèrpret PHP mostra un bon rendiment, tot i que hi ha una diferència, és insignificant i, potser, encara ho serà menys quan s'utilitza la càrrega prèvia a PHP 7.4, per no parlar de JIT a PHP 8.

Posem-nos en marxa. Tarantool 2.0 va afegir suport per a SQL. Intentem realitzar operacions de selecció, inserció, actualització i supressió mitjançant el protocol SQL i comparem els resultats amb els equivalents noSQL (binaris):

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Els resultats SQL no són gaire impressionants (permeteu-me que us recordi que encara estem provant el mode síncron). Tanmateix, no m'enfadaria amb això abans d'hora, el suport SQL encara està en desenvolupament actiu (fa relativament recentment, per exemple, s'ha afegit suport declaracions preparades) i, a jutjar per la llista problemes, el motor SQL patirà una sèrie d'optimitzacions en el futur.

Asíncron

Bé, ara vegem com l'extensió Async ens pot ajudar a millorar els resultats anteriors. Per escriure programes asíncrons, l'extensió proporciona una API basada en corrutines, que farem servir. Descobrim empíricament que el nombre òptim de corrutines per al nostre entorn és 25:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
"Repartiu" 10,000 operacions en 25 corrutines i mireu què passa:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
El nombre d'operacions per segon va augmentar més de 3 vegades per tarantool-php/client!

Malauradament, el connector PECL no va començar amb ext-async.

Què passa amb SQL?

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Com podeu veure, en mode asíncron la diferència entre el protocol binari i SQL es va situar dins del marge d'error.

Swoole

De nou esbrinem el nombre òptim de corrutines, aquesta vegada per a Swoole:
Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Aturem-nos a 25. Repetim el mateix truc que amb l'extensió Async: distribuïu 10,000 operacions entre 25 corrutines. A més, afegirem una altra prova en la qual dividirem tot el treball en 2 dos processos (és a dir, cada procés realitzarà 5,000 operacions en 25 corrutines). Els processos es crearan utilitzant SwooleProcess.

Resultats:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Swole mostra un resultat lleugerament inferior en comparació amb Async quan s'executa en un procés, però amb 2 processos la imatge canvia dràsticament (el número 2 no es va escollir per casualitat; a la meva màquina, van ser 2 processos els que van mostrar el millor resultat).

Per cert, l'extensió Async també té una API per treballar amb processos, però allà no vaig notar cap diferència amb l'execució de benchmarks en un o més processos (és possible que m'he equivocat en algun lloc).

Protocol SQL vs binari:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Igual que amb Async, la diferència entre operacions binàries i SQL s'elimina en mode asíncron.

Paral · lel

Com que l'extensió paral·lel no tracta de corrutines, sinó de fils, mesurem el nombre òptim de fils paral·lels:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Té 16 a la meva màquina. Executem punts de referència de connectors en 16 fils paral·lels:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Com podeu veure, el resultat és encara millor que amb les extensions asíncrones (sense comptar Swoole en execució en 2 processos). Tingueu en compte que per al connector PECL, les operacions Actualització i Upsert estan buides. Això es deu al fet que aquestes operacions van fallar amb un error: no sé si va ser culpa d'ext-parallel, ext-tarantool o ambdues coses.

Ara comparem el rendiment SQL:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel
Observeu la similitud amb el gràfic dels connectors que funcionen de manera sincrònica?

Junts

I, finalment, resumim tots els resultats en un gràfic per veure la imatge general de les extensions provades. Afegim només una prova nova al gràfic, que encara no hem fet: executarem corrutines asincàries en paral·lel mitjançant Parallel*. La idea d'integrar les extensions anteriors ja està discutit autors, però no s'ha arribat a un consens, ho haureu de fer vosaltres mateixos.

* No va ser possible llançar coroutines Swoole amb Parallel sembla que aquestes extensions són incompatibles.

Així doncs, els resultats finals:

Acceleració de connectors PHP per a Tarantool mitjançant Async, Swoole i Parallel

En lloc d'una conclusió

Al meu entendre, els resultats van resultar bastant dignes, i per alguna raó estic segur que aquest no és el límit! Tant si necessiteu decidir això en un projecte real només per a vosaltres mateixos, només diré que per a mi va ser un experiment interessant que us permet avaluar quant podeu "extreure" un connector TCP síncron amb un esforç mínim. Si teniu idees per millorar els punts de referència, estaré encantat de considerar la vostra sol·licitud d'extracció. Tot el codi amb instruccions de llançament i resultats es publica en un separat repositoris.

Font: www.habr.com

Afegeix comentari