Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

In questo articolo viene esaminato un caso di accelerazione di un'applicazione browser attraverso la sostituzione dei calcoli JavaScript con WebAssembly.

WebAssembly — che cos'è?

In breve, è un formato binario di istruzioni per una macchina virtuale stack. Spesso il termine Wasm (abbreviazione) è usato per riferirsi a un linguaggio di programmazione, ma non è corretto. Il formato delle istruzioni viene eseguito nel browser insieme a JavaScript.

È importante sottolineare che WebAssembly può essere generato compilando sorgenti in linguaggi come C/C++, Rust, Go. Qui si applica la tipizzazione statica e il cosiddetto modello di memoria piatta. Il codice, come detto sopra, è memorizzato in un formato binario compatto, il che consente una velocità di esecuzione quasi paragonabile a quella di un'applicazione avviata tramite riga di comando. Queste caratteristiche hanno contribuito all'aumento della popolarità di WebAssembly.

Ricordiamo: per tutti i lettori di «Habr» — sconto di 10.000 rubli per l'iscrizione a qualsiasi corso Skillbox con il codice promozionale «Habr».

Skillbox consiglia: Corso pratico «Sviluppatore mobile PRO».

Attualmente, Wasm è utilizzato in molte applicazioni, da giochi come Doom 3 a applicazioni portate nel web come Autocad e Figma. Wasm è utilizzato anche in ambito di calcoli serverless.

In questo articolo verrà fornito un esempio di utilizzo di Wasm per accelerare un servizio web analitico. Per chiarezza, abbiamo preso un'applicazione funzionante, scritta in C, che verrà compilata in WebAssembly. Il risultato sarà utilizzato per sostituire le sezioni JavaScript poco performanti.

Trasformazione dell'applicazione

Nell'esempio sarà utilizzato il servizio browser fastq.bio, progettato per genetisti. Questo strumento consente di valutare la qualità della sequenza (del decoding) del DNA.

Ecco un esempio di applicazione in funzione:

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

Non vale la pena entrare nei dettagli del processo, poiché sono piuttosto complessi per i non esperti, ma in sintesi, gli scienziati possono comprendere grazie all'infografica sopra citata se il processo di sequenziamento del DNA è avvenuto senza intoppi e quali problemi si sono presentati.

Questo servizio ha delle alternative, programmi desktop. Tuttavia, fastq.bio consente di velocizzare il lavoro, visualizzando i dati. Nella maggior parte degli altri casi è necessario saper lavorare con la linea di comando, ma non tutti i genetisti possiedono l'esperienza necessaria.

Funziona tutto in modo semplice. In input ci sono dati rappresentati come file di testo. Questo file è generato da strumenti specializzati per il sequenziamento. All'interno del file si trova un elenco di sequenze di DNA e una valutazione della qualità per ogni nucleotide. Il formato del file è .fastq, da cui il servizio ha preso il nome.

Implementazione in JavaScript

Il primo passo dell'utente quando utilizza fastq.bio è la selezione del file appropriato. Utilizzando l'oggetto File, l'applicazione legge un campione casuale di dati dal file e elabora questo pacchetto. Il compito di JavaScript qui è eseguire operazioni stringa semplici e calcolare i parametri. Uno di essi è il numero di nucleotidi A, C, G e T in diversi frammenti di DNA.

Dopo il calcolo dei parametri necessari, vengono visualizzati utilizzando Plotly.js, e il servizio inizia a lavorare con un nuovo campione di dati. La suddivisione in frammenti è stata fatta per migliorare la qualità dell'UX. Se si lavora con tutti i dati contemporaneamente, il processo si bloccherà per un po' di tempo, poiché i file con i risultati del sequenziamento occupano centinaia di gigabyte di spazio di archiviazione. Il servizio invece prende sezioni di dati di dimensioni comprese tra 0,5 e 1 MB e lavora su di esse passo dopo passo, costruendo dati grafici.

Ecco come funziona:

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

Nettamente rosso, rettangolo contiene l'algoritmo di trasformazione delle stringhe per ottenere la visualizzazione. Questa è la parte più pesante in termini di calcolo del servizio. Potrebbe essere interessante sostituirla con Wasm.

Testiamo WebAssembly

Per valutare la possibilità di utilizzare Wasm, il team del progetto ha cercato soluzioni pronte per creare metriche di QC (QC - controllo qualità) basate su file fastq. La ricerca è stata condotta tra strumenti scritti in C, C++ o Rust, in modo da poter portare il codice su WebAssembly. Inoltre, lo strumento non deve essere "grezzo", era necessaria una soluzione già testata dagli scienziati.

Alla fine, la scelta è caduta su seqtk. L'applicazione è abbastanza popolare, è open-source, il linguaggio di base è C.

Prima di compilarlo in Wasm, è utile esaminare il principio di compilazione di seqtk per desktop. In base al Makefile, ecco cosa è necessario:

# Compile to binary
$ gcc seqtk.c 
   -o seqtk 
   -O2 
   -lm 
   -lz

In linea di principio, è possibile compilare seqtk usando Emscripten. Se non è disponibile, ci si arrangia in questo modo con Docker.

$ docker pull robertaboukhalil/emsdk:1.38.26
$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26

Se si desidera può essere compilato anche manualmente, ma ci vuole tempo.

All'interno del contenitore non ci sono problemi a usare emcc come alternativa a gcc:

# Compile to WebAssembly
$ emcc seqtk.c 
    -o seqtk.js 
    -O2 
    -lm 
    -s USE_ZLIB=1 
    -s FORCE_FILESYSTEM=1

Minimo le modifiche:

Invece dell'output in un file binario, Emscripten utilizza .wasm e .js per generare file utilizzati per eseguire il modulo WebAssembly.

Per supportare la libreria zlib, si utilizza il flag USE_ZLIB. La libreria è comune e portata su WebAssembly, e Emscripten la include nel progetto.

Si attiva il file system virtuale di Emscripten. Questo FS simile a POSIX, opera in memoria all'interno del browser. Quando la pagina viene aggiornata, la memoria viene liberata.

Per capire perché sia necessario un file system virtuale, è utile confrontare il modo di eseguire seqtk dalla riga di comando con il modo di eseguire un modulo WebAssembly compilato.

# On the command line
$ ./seqtk fqchk data.fastq
 
# In the browser console
> Module.callMain(["fqchk", "data.fastq"])

Accedere al file system virtuale è necessario per non dover riscrivere seqtk per l'input come stringa invece che come file. In questo caso, il frammento di dati è visualizzato come file data.fastq nel FS virtuale con la chiamata a main() seqtk.

Ecco la nuova architettura:

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

L'immagine dimostra che invece delle elaborazioni nel thread principale del browser vengono utilizzati WebWorkers. Questo metodo consente di eseguire calcoli in un thread in background, senza compromettere la reattività del browser. Inoltre, il controllore WebWorker avvia il Worker, gestendo la sua interazione con il thread principale.

Il comando seqtk viene avviato tramite Worker sul file montato. Al termine dell'esecuzione, il Worker restituisce il risultato in forma di Promise. Quando il messaggio viene ricevuto dal thread principale, il risultato è usato per aggiornare i grafici. E così tramite diverse iterazioni.

Come va la performance di WebAssembly?

Per valutare il cambiamento delle prestazioni, il team del progetto ha utilizzato il parametro del numero di operazioni di lettura al secondo. Il tempo di costruzione dei grafici interattivi non è preso in considerazione, poiché in entrambe le implementazioni si utilizza JavaScript.

Utilizzando la soluzione "pronta all'uso", il guadagno di prestazioni è stato di nove volte.

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

È un ottimo risultato, ma si è scoperto che c'era la possibilità di ottimizzarlo ulteriormente. Infatti, un gran numero di risultati dell'analisi QC non viene utilizzato da seqtk, quindi possono essere rimossi. Se ciò viene fatto, il risultato rispetto a JS migliora di 13 volte.

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

Si è riusciti a ottenerlo semplicemente commentando i comandi printf().

Ma non è tutto. Il fatto è che in questa fase fastq.bio riceve i risultati dall'analisi chiamando varie funzioni C. Ognuna di esse calcola il proprio insieme di caratteristiche, quindi ogni frammento del file è stato letto due volte.

Per affrontare questo problema, è stato deciso di combinare due funzioni in una. Di conseguenza, le prestazioni sono aumentate di 20 volte.

Come abbiamo accelerato l'applicazione web di 20 volte utilizzando WebAssembly

Vale la pena notare che un risultato così straordinario non si può sempre ottenere. In alcuni casi, le prestazioni diminuiscono, quindi è necessario valutare ogni singolo caso.

In conclusione, si può dire che Wasm offre effettivamente la possibilità di migliorare le prestazioni dell'applicazione, ma va utilizzato con saggezza.

Skillbox consiglia:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS - ProHoster