Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel

No ecosistema PHP actualmente hai dous conectores para traballar co servidor Tarantool: esta é a extensión oficial PECL tarantool/tarantool-php, escrito en C, e tarantool-php/cliente, escrito en PHP. Son o autor deste último.

Neste artigo, gustaríame compartir os resultados das probas de rendemento de ambas as bibliotecas e mostrar como, con cambios mínimos no código, podes conseguir un aumento de rendemento de 3 a 5 (en probas sintéticas!).

Que probaremos?

Probaremos os mencionados anteriormente sincrónico conectores que funcionan de forma asíncrona, en paralelo e de forma asíncrona en paralelo. 🙂 Tampouco queremos tocar o código dos propios conectores. Actualmente hai varias extensións dispoñibles para conseguir o que queres:

  • Swoole ― un marco asíncrono de alto rendemento para PHP. Usado por xigantes de Internet como Alibaba e Baidu. Desde a versión 4.1.0 apareceu un método máxico SwooleRuntime::enableCoroutine(), que lle permite "converter bibliotecas de rede PHP síncronas en asíncronas cunha liña de código".
  • Async era ata hai pouco unha extensión moi prometedora para o traballo asíncrono en PHP. Por que ata hai pouco? Desafortunadamente, por un motivo descoñecido para min, o autor eliminou o repositorio e o destino futuro do proxecto non está claro. Terei que usalo un de garfos. Do mesmo xeito que Swoole, esta extensión permítelle encender facilmente os pantalóns cun toque do pulso para habilitar a asincronía substituíndo a implementación estándar dos fluxos TCP e TLS polas súas versións asíncronas. Isto faise a través da opción "async.tcp = 1«.
  • Paralela ― unha extensión bastante nova do coñecido Joe Watkins, autor de bibliotecas como phpdbg, apcu, pthreads, pcov, uopz. A extensión proporciona unha API para multithreading en PHP e sitúase como un substituto para pthreads. Unha limitación significativa da biblioteca é que só funciona coa versión ZTS (Zend Thread Safe) de PHP.

Como imos probar?

Imos lanzar unha instancia de Tarantool co rexistro de escritura anticipada desactivado (wal_mode = ningún) e aumento do búfer de rede (lectura = 1 * 1024 * 1024). A primeira opción eliminará o traballo co disco, a segunda permitirá ler máis solicitudes do búfer do sistema operativo e minimizar así o número de chamadas ao sistema.

Para os benchmarks que traballan con datos (inserción, eliminación, lectura, etc.), antes de iniciar o benchmark, (re)crearase un espazo memtx, no que os valores do índice primario son creados por un xerador de valores enteiros ordenados. (secuencia).
O espazo DDL ten o seguinte aspecto:

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

Se é necesario, antes de executar o benchmark, o espazo énchese con 10,000 tuplas do formulario

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

Accédese ás tuplas mediante un valor de chave aleatorio.

O propio benchmark é unha única solicitude ao servidor, que se executa 10,000 veces (revolucións), que, á súa vez, execútanse en iteracións. As iteracións repítense ata que todas as desviacións de tempo entre 5 iteracións estean dentro dun erro aceptable do 3 %*. Despois diso, tómase o resultado medio. Hai unha pausa de 1 segundo entre iteracións para evitar que o procesador se estrague. O colector de lixo de Lua está desactivado antes de cada iteración e vese obrigado a iniciarse despois de que se complete. O proceso PHP lánzase só coas extensións necesarias para o benchmark, co búfer de saída activado e o colector de lixo desactivado.

* O número de revolucións, iteracións e limiar de erro pódese cambiar na configuración de referencia.

Entorno de proba

Os resultados publicados a continuación fixéronse nun sistema operativo MacBookPro (2015), Fedora 30 (versión do núcleo 5.3.8-200.fc30.x86_64). Tarantool lanzouse no docker co parámetro "--network host".

Versións do paquete:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, compilación a872fc2f86
PHP: 7.3.11 (cli) (construído: 22 de outubro de 2019 ás 08:11:04)
tarantool/cliente: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ parche para 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-paralelo: 1.1.3

* Desafortunadamente, o conector oficial non funciona coa versión de PHP > 7.2. Para compilar e executar a extensión en PHP 7.3, tiven que usar parche.

Descubrimentos

Modo sincrónico

O protocolo Tarantool usa un formato binario Paquete de mensaxes para serializar mensaxes. No conector PECL, a serialización escóndese nas profundidades da biblioteca e afecta ao proceso de codificación do código do usuario. non parece posible. Un conector PHP puro, pola contra, ofrece a posibilidade de personalizar o proceso de codificación estendendo o codificador estándar ou utilizando a súa propia implementación. Hai dous codificadores dispoñibles fóra da caixa, un está baseado msgpack/msgpack-php (extensión oficial MessagePack PECL), a outra está activada rybakit/msgpack (en PHP puro).

Antes de comparar conectores, mediremos o rendemento dos codificadores MessagePack para o conector PHP e en probas posteriores usaremos aquel que amose o mellor resultado:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Aínda que a versión de PHP (Pure) é inferior á extensión PECL en velocidade, en proxectos reais aínda recomendaría usala. rybakit/msgpack, porque na extensión oficial MessagePack a especificación do formato só se implementa parcialmente (por exemplo, non hai soporte para tipos de datos personalizados, sen o cal non poderás usar Decimal - un novo tipo de datos introducido en Tarantool 2.3) e ten un número doutros problemas (incluíndo problemas de compatibilidade con PHP 7.4). Ben, en xeral, o proxecto parece abandonado.

Entón, imos medir o rendemento dos conectores en modo síncrono:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Como se pode ver no gráfico, o conector PECL (Tarantool) mostra un mellor rendemento en comparación co conector PHP (Cliente). Pero isto non é de estrañar, dado que este último, ademais de implementarse nunha linguaxe máis lenta, en realidade fai máis traballo: créase un novo obxecto con cada chamada. Solicitude и Resposta (no caso de Select - tamén Criterios, e no caso de Actualizar/Subir ― operacións), entidades separadas Conexión, Empaquetador и Adestrador tamén engaden sobrecarga. Obviamente, a flexibilidade ten un prezo. Non obstante, en xeral, o intérprete PHP mostra un bo rendemento, aínda que hai unha diferenza, é insignificante e, quizais, será aínda menos cando se usa a carga previa en PHP 7.4, sen esquecer JIT en PHP 8.

Sigamos adiante. Tarantool 2.0 introduciu soporte SQL. Intentemos realizar operacións de selección, inserción, actualización e eliminación mediante o protocolo SQL e comparemos os resultados cos equivalentes noSQL (binarios):

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Os resultados de SQL non son moi impresionantes (permítanme recordarlle que aínda estamos probando o modo sincrónico). Non obstante, non me molestaría con isto antes de tempo; o soporte SQL aínda está en desenvolvemento activo (relativamente recentemente, por exemplo, engadiuse soporte declaracións preparadas) e, a xulgar pola lista cuestións, o motor SQL sufrirá unha serie de optimizacións no futuro.

Asíncronas

Ben, agora vexamos como a extensión Async pode axudarnos a mellorar os resultados anteriores. Para escribir programas asíncronos, a extensión proporciona unha API baseada en corrutinas, que utilizaremos. Descubrimos empíricamente que o número óptimo de corrutinas para o noso entorno é 25:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
"Difunde" 10,000 operacións en 25 rutinas e mira o que sucede:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
O número de operacións por segundo aumentou máis de 3 veces para tarantool-php/cliente!

Lamentablemente, o conector PECL non comezou con ext-async.

Que pasa con SQL?

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Como podes ver, no modo asíncrono a diferenza entre o protocolo binario e SQL quedou dentro da marxe de erro.

Swoole

De novo descubrimos o número óptimo de corrutinas, esta vez para Swoole:
Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Detémonos en 25. Repetimos o mesmo truco que coa extensión Async: distribúe 10,000 operacións entre 25 corrutinas. Ademais, engadiremos outra proba na que dividiremos todo o traballo en 2 dous procesos (é dicir, cada proceso realizará 5,000 operacións en 25 corrutinas). Os procesos crearanse usando Proceso Swoole.

Resultados:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Swole mostra un resultado lixeiramente inferior en comparación con Async cando se executa nun proceso, pero con 2 procesos a imaxe cambia drasticamente (o número 2 non foi elixido por casualidade; na miña máquina, foron 2 procesos os que mostraron o mellor resultado).

Por certo, a extensión Async tamén ten unha API para traballar con procesos, pero alí non notei ningunha diferenza ao executar benchmarks nun ou máis procesos (é posible que me despistei nalgún lugar).

Protocolo SQL vs binario:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Do mesmo xeito que con Async, a diferenza entre as operacións binarias e SQL elimínase no modo asíncrono.

Paralela

Dado que a extensión Parallel non se trata de corrutinas, senón de fíos, imos medir o número óptimo de fíos paralelos:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
É igual a 16 na miña máquina. Imos executar benchmarks de conectores en 16 fíos paralelos:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Como podes ver, o resultado é aínda mellor que coas extensións asíncronas (sen contar Swoole en execución en 2 procesos). Teña en conta que para o conector PECL, as operacións Actualización e Upsert están baleiras. Isto débese ao feito de que estas operacións fallaron cun erro: non sei se foi por culpa de ext-parallel, ext-tarantool ou de ambos.

Agora imos comparar o rendemento de SQL:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel
Observa a semellanza coa gráfica dos conectores que funcionan de forma sincronizada?

Xuntos

E, finalmente, resumimos todos os resultados nun gráfico para ver a imaxe xeral das extensións probadas. Engademos só unha proba nova ao gráfico, que aínda non fixemos: executemos as rutinas asíncronas en paralelo usando Parallel*. A idea de integrar as extensións anteriores xa está discutido autores, pero non se chegou a un consenso, terás que facelo ti mesmo.

* Non foi posible lanzar coroutines Swoole con Parallel; parece que estas extensións son incompatibles.

Así, os resultados finais:

Acelerando os conectores PHP para Tarantool usando Async, Swoole e Parallel

En vez de unha conclusión

Na miña opinión, os resultados resultaron ser bastante dignos, e por algún motivo estou seguro de que este non é o límite. Se necesitas decidir isto nun proxecto real só por ti mesmo, só direi que para min foi un experimento interesante que che permite avaliar canto podes "espremer" un conector TCP sincrónico cun mínimo esforzo. Se tes ideas para mellorar os puntos de referencia, estarei encantado de considerar a túa solicitude de extracción. Todo o código con instrucións de lanzamento e resultados publícase nun separado repositorios.

Fonte: www.habr.com

Engadir un comentario