Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel

Nell'ecosistema PHP attualmente sono presenti due connettori per lavorare con il server Tarantool: questa è l'estensione PECL ufficiale tarantool/tarantool-php, scritto in C, e tarantool-php/client, scritto in PHP. Di quest'ultimo sono l'autore.

In questo articolo, vorrei condividere i risultati dei test delle prestazioni di entrambe le librerie e mostrare come, con modifiche minime al codice, è possibile ottenere un aumento delle prestazioni di 3-5 (sui test sintetici!).

Cosa testeremo?

Testeremo quelli menzionati sopra sincrono connettori che funzionano in modo asincrono, in parallelo e in parallelo asincrono. 🙂 Inoltre non vogliamo toccare il codice dei connettori stessi. Attualmente sono disponibili diverse estensioni per ottenere ciò che desideri:

  • swoole ― un framework asincrono ad alte prestazioni per PHP. Utilizzato da giganti di Internet come Alibaba e Baidu. Dalla versione 4.1.0 è apparso un metodo magico SwooleRuntime::enableCoroutine(), che ti consente di "convertire le librerie di rete PHP sincrone in asincrone con una riga di codice".
  • Fino a poco tempo fa Async era un'estensione molto promettente per il lavoro asincrono in PHP. Perché fino a poco tempo fa? Sfortunatamente, per un motivo a me sconosciuto, l'autore ha cancellato il repository e il destino futuro del progetto non è chiaro. dovrò usarlo uno dalle forcelle. Come Swoole, questa estensione ti consente di accenderti facilmente i pantaloni con un semplice movimento del polso per abilitare l'asincronia sostituendo l'implementazione standard dei flussi TCP e TLS con le loro versioni asincrone. Questo viene fatto attraverso l'opzione “asincrono.tcp = 1«.
  • Parallel ― un'estensione abbastanza nuova del noto Joe Watkins, autore di librerie come phpdbg, apcu, pthreads, pcov, uopz. L'estensione fornisce un'API per il multithreading in PHP e si posiziona come sostituto di pthreads. Una limitazione significativa della libreria è che funziona solo con la versione ZTS (Zend Thread Safe) di PHP.

Come testeremo?

Lanciamo un'istanza di Tarantool con la registrazione write-ahead disabilitata (wal_mode = nessuno) e aumento del buffer di rete (lettura anticipata = 1 * 1024 * 1024). La prima opzione eliminerà il lavoro con il disco, la seconda consentirà di leggere più richieste dal buffer del sistema operativo e quindi ridurre al minimo il numero di chiamate di sistema.

Per i benchmark che funzionano con i dati (inserimento, cancellazione, lettura, ecc.), prima di iniziare il benchmark, verrà (ri)creato uno spazio memtx, in cui i valori degli indici primari vengono creati da un generatore di valori interi ordinati ​(sequenza).
Lo spazio DDL si presenta così:

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 necessario, prima di eseguire il benchmark, lo spazio viene riempito con 10,000 tuple del form

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

Si accede alle tuple utilizzando un valore di chiave casuale.

Il benchmark stesso è una singola richiesta al server, che viene eseguita 10,000 volte (rivoluzioni), che, a loro volta, vengono eseguite in iterazioni. Le iterazioni vengono ripetute fino a quando tutte le deviazioni temporali tra 5 iterazioni rientrano in un errore accettabile del 3%*. Successivamente, viene preso il risultato medio. C'è una pausa di 1 secondo tra le iterazioni per evitare la limitazione del processore. Il garbage collector di Lua viene disabilitato prima di ogni iterazione ed è costretto ad avviarsi dopo il suo completamento. Il processo PHP viene avviato solo con le estensioni necessarie per il benchmark, con il buffering dell'output abilitato e il garbage collector disabilitato.

* Il numero di giri, iterazioni e soglia di errore possono essere modificati nelle impostazioni del benchmark.

Ambiente di test

I risultati pubblicati di seguito sono stati realizzati su un MacBookPro (2015), sistema operativo - Fedora 30 (versione del kernel 5.3.8-200.fc30.x86_64). Tarantool è stato lanciato nella finestra mobile con il parametro "--network host".

Versioni del pacchetto:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, crea a872fc2f86
PHP: 7.3.11 (cli) (creato: 22 ottobre 2019 08:11:04)
tarantool/client: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch per 7.3)*
ext-msgpack: 2.0.3
ext-asincrono: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-parallelo: 1.1.3

* Sfortunatamente, il connettore ufficiale non funziona con la versione PHP > 7.2. Per compilare ed eseguire l'estensione su PHP 7.3, ho dovuto utilizzare toppa.

Giudizio

Modalità sincrona

Il protocollo Tarantool utilizza un formato binario Pacchetto messaggi per serializzare i messaggi. Nel connettore PECL, la serializzazione è nascosta nelle profondità della libreria e influenza il processo di codifica dal codice utente impossibile. Un connettore PHP puro, al contrario, offre la possibilità di personalizzare il processo di codifica estendendo il codificatore standard o utilizzando la propria implementazione. Sono disponibili due codificatori pronti all'uso, uno dei quali è basato su msgpack/msgpack-php (estensione ufficiale MessagePack PECL), l'altro è attivo rybakit/msgpack (in puro PHP).

Prima di confrontare i connettori, misureremo le prestazioni dei codificatori MessagePack per il connettore PHP e in ulteriori test utilizzeremo quello che mostra il miglior risultato:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Sebbene la versione PHP (Pure) sia inferiore all'estensione PECL in termini di velocità, nei progetti reali consiglierei comunque di utilizzarla rybakit/msgpack, perché nell'estensione ufficiale MessagePack la specifica del formato è implementata solo parzialmente (ad esempio, non c'è supporto per i tipi di dati personalizzati, senza i quali non sarà possibile utilizzare Decimal - un nuovo tipo di dati introdotto in Tarantool 2.3) e ha un numero di altri проблем (inclusi problemi di compatibilità con PHP 7.4). Ebbene, in generale, il progetto sembra abbandonato.

Quindi, misuriamo le prestazioni dei connettori in modalità sincrona:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Come si può vedere dal grafico, il connettore PECL (Tarantool) mostra prestazioni migliori rispetto al connettore PHP (Client). Ma questo non sorprende, visto che quest'ultimo, oltre ad essere implementato in un linguaggio più lento, in realtà fa più lavoro: ad ogni chiamata viene creato un nuovo oggetto RICHIEDI и Risposta (nel caso di Select - anche Criterie nel caso di Aggiorna/Upsert ― Operazioni), entità separate Connessione, Imballatore и Handler aggiungono anche spese generali. Ovviamente la flessibilità ha un prezzo. Tuttavia, in generale, l'interprete PHP mostra buone prestazioni, anche se c'è una differenza, è insignificante e, forse, sarà ancora inferiore quando si utilizza il precaricamento in PHP 7.4, per non parlare di JIT in PHP 8.

Andiamo avanti. Tarantool 2.0 ha introdotto il supporto SQL. Proviamo ad eseguire operazioni di selezione, inserimento, aggiornamento ed eliminazione utilizzando il protocollo SQL e confrontiamo i risultati con gli equivalenti noSQL (binari):

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
I risultati SQL non sono molto impressionanti (permettetemi di ricordarvi che stiamo ancora testando la modalità sincrona). Tuttavia, non mi arrabbierei per questo in anticipo; il supporto SQL è ancora in fase di sviluppo attivo (relativamente recentemente, ad esempio, è stato aggiunto il supporto dichiarazioni preparate) e, a giudicare dall'elenco sicurezza, il motore SQL verrà sottoposto a una serie di ottimizzazioni in futuro.

Asincrono

Bene, ora vediamo come l'estensione Async può aiutarci a migliorare i risultati sopra. Per scrivere programmi asincroni, l'estensione fornisce un'API basata su coroutine, che utilizzeremo. Scopriamo empiricamente che il numero ottimale di coroutine per il nostro ambiente è 25:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
"Distribuisci" 10,000 operazioni su 25 coroutine e guarda cosa succede:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Il numero di operazioni al secondo è aumentato di oltre 3 volte tarantool-php/client!

Purtroppo, il connettore PECL non è iniziato con ext-async.

E che dire dell'SQL?

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Come puoi vedere, in modalità asincrona la differenza tra il protocollo binario e SQL rientra nel margine di errore.

swoole

Ancora una volta scopriamo il numero ottimale di coroutine, questa volta per Swoole:
Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Fermiamoci a 25. Ripetiamo lo stesso trucco dell'estensione Async: distribuiamo 10,000 operazioni tra 25 coroutine. Inoltre, aggiungeremo un altro test in cui divideremo tutto il lavoro in 2 due processi (ovvero ogni processo eseguirà 5,000 operazioni in 25 coroutine). I processi verranno creati utilizzando Swoole Process.

Risultati:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Swole mostra un risultato leggermente inferiore rispetto ad Async quando eseguito in un processo, ma con 2 processi l'immagine cambia drasticamente (il numero 2 non è stato scelto per caso; sulla mia macchina, erano 2 processi a mostrare il risultato migliore).

A proposito, l'estensione Async ha anche un'API per lavorare con i processi, ma lì non ho notato alcuna differenza rispetto all'esecuzione di benchmark in uno o più processi (è possibile che abbia sbagliato da qualche parte).

Protocollo SQL vs binario:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Come con Async, la differenza tra operazioni binarie e SQL viene eliminata in modalità asincrona.

Parallel

Poiché l'estensione Parallel non riguarda le coroutine, ma i thread, misuriamo il numero ottimale di thread paralleli:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
È uguale a 16 sulla mia macchina. Eseguiamo i benchmark dei connettori su 16 thread paralleli:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Come puoi vedere, il risultato è persino migliore rispetto alle estensioni asincrone (senza contare Swoole in esecuzione su 2 processi). Tieni presente che per il connettore PECL le operazioni Update e Upsert sono vuote. Ciò è dovuto al fatto che queste operazioni sono fallite con un errore: non so se sia colpa di ext-parallel, ext-tarantool o di entrambi.

Ora confrontiamo le prestazioni SQL:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel
Notate la somiglianza con il grafico per i connettori che funzionano in modo sincrono?

Insieme

Infine, riassumiamo tutti i risultati in un grafico per vedere il quadro generale delle estensioni testate. Aggiungiamo solo un nuovo test al grafico, cosa che non abbiamo ancora fatto: eseguiamo le coroutine Async in parallelo utilizzando Parallel*. L'idea di integrare le estensioni di cui sopra c'è già è stato discusso autori, ma non è stato raggiunto un consenso, dovrai farlo tu stesso.

* Non è stato possibile avviare le coroutine Swoole con Parallel; sembra che queste estensioni siano incompatibili.

Quindi, i risultati finali:

Accelerazione dei connettori PHP per Tarantool utilizzando Async, Swoole e Parallel

Invece di una conclusione

Secondo me, i risultati si sono rivelati abbastanza degni e per qualche motivo sono sicuro che questo non sia il limite! Se devi deciderlo in un progetto reale esclusivamente per te stesso, dirò solo che per me è stato un esperimento interessante che ti permette di valutare quanto puoi “spremere” da un connettore TCP sincrono con il minimo sforzo. Se hai idee per migliorare i benchmark, sarò felice di prendere in considerazione la tua richiesta di pull. Tutto il codice con le istruzioni di lancio e i risultati è pubblicato in un file separato repository.

Fonte: habr.com

Aggiungi un commento