Come abbiamo accelerato un'app web di 20 volte con WebAssembly

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

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

Cos'è WebAssembly?

In breve, è un formato binario di istruzioni per una macchina virtuale a stack. Spesso il nome abbreviato Wasm viene definito come un linguaggio di programmazione, ma non è corretto. Il formato delle istruzioni viene eseguito nel browser insieme a JavaScript.

È importante notare che WebAssembly può essere generato compilando sorgenti in linguaggi come C/C++, Rust, Go. Qui viene utilizzata 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 rapida esecuzione, quasi come se l'app fosse avviata tramite riga di comando. Queste caratteristiche hanno contribuito alla crescente 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, WebAssembly (Wasm) è utilizzato in molte applicazioni, dai giochi come Doom 3 alle applicazioni web portate come Autocad e Figma. Wasm è impiegato anche nel settore del serverless computing.

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

Trasformazione dell'applicazione

Nell'esempio verrà utilizzato il servizio browser fastq.bio, progettato per genetisti. Questo strumento consente di valutare la qualità del sequenziamento (decodifica) del DNA.

Ecco un esempio dell'applicazione in azione:

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

I dettagli del processo non sono necessari, poiché sono piuttosto complessi per i non specialisti, ma in breve, gli scienziati possono capire dalla grafica sopra riportata se il processo di sequenziamento del DNA è andato bene e quali problemi sono emersi.

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

Tutto funziona in modo semplice. In input ci sono dati presentati sotto forma di file di testo. Questo file è generato da strumenti specializzati per il sequenziamento. Nel file è presente un elenco di sequenze DNA e una valutazione della qualità per ciascun nucleotide. Il formato del file è .fastq, da qui il nome del servizio.

Implementazione in JavaScript

Il primo passo per l'utente che lavora con fastq.bio è la scelta 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 è l'esecuzione di semplici operazioni su stringhe e il conteggio delle metriche. Uno di questi è il numero di nucleotidi A, C, G e T in diversi frammenti di DNA.

Dopo aver calcolato i parametri necessari, vengono visualizzati tramite Plotly.js, e il servizio inizia a lavorare con un nuovo campione di dati. La suddivisione in frammenti è stata effettuata per migliorare la qualità dell'UX. Se si lavora con tutti i dati contemporaneamente, il processo si blocca per un po', poiché i file dei risultati di sequenziamento occupano centinaia di gigabyte di spazio. Tuttavia, il servizio gestisce porzioni di dati che vanno da 0,5 a 1 MB, lavorando su di esse passo dopo passo, costruendo dati grafici.

Ecco come funziona:

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

Nel rettangolo rosso si trova l'algoritmo di trasformazione delle stringhe per ottenere la visualizzazione. Questa è la parte del servizio più impegnativa dal punto di vista computazionale. Sarebbe interessante provare a sostituirla con Wasm.

Testiamo WebAssembly

Per valutare la possibilità di utilizzare Wasm, il team del progetto ha cercato soluzioni pronte per creare una metrica QC (QC - quality control) utilizzando file fastq. La ricerca è stata condotta tra gli strumenti scritti in C, C++ o Rust, in modo da poter portare il codice su WebAssembly. Inoltre, lo strumento non doveva essere "grezzo"; era necessario che fosse un servizio già collaudato dagli scienziati.

Di conseguenza, è stata effettuata la scelta in favore di seqtk. L'applicazione è piuttosto popolare, è open-source e il linguaggio sorgente è il C.

Prima di convertirlo in Wasm, è utile esaminare il principio di compilazione di seqtk per desktop. Secondo il Makefile, ecco ciò che occorre:

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

In linea di principio, è possibile compilare seqtk utilizzando Emscripten. Se non disponibile, possiamo ricorrere a Docker.

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

Se si desidera è possibile compilarlo anche autonomamente, ma questo richiede tempo.

All'interno del contenitore, è possibile utilizzare emcc come alternativa a gcc senza problemi:

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

Le modifiche sono minime:

Invece di produrre un file binario, Emscripten utilizza .wasm e .js per generare i file, che vengono utilizzati per eseguire il modulo WebAssembly.

Per supportare la libreria zlib, viene utilizzato il flag USE_ZLIB. La libreria è diffusa ed è stata portata su WebAssembly, e Emscripten la include nel progetto.

Viene attivato il file system virtuale di Emscripten. Questo è un FS simile a POSIX, che opera in memoria dentro il browser. Quando la pagina viene aggiornata, la memoria viene svuotata.

Per comprendere a cosa serve un file system virtuale, è utile confrontare il modo di eseguire seqtk dalla riga di comando con il modo di eseguire un modulo WebAssembly già 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 evitare di riscrivere seqtk per l'input da stringa anziché da file. In questo caso, un frammento di dati è rappresentato come un file data.fastq nel FS virtuale con l'invocazione su di esso della funzione main() di seqtk.

Ecco la nuova architettura:

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

L’immagine mostra che invece dei calcoli nel thread principale del browser si utilizza WebWorkers. Questo approccio consente di eseguire i calcoli in un thread in background senza compromettere la reattività del browser. Il controller WebWorker avvia il Worker, gestendo la sua interazione con il thread principale.

Il comando seqtk viene eseguito tramite Worker sul file montato. Una volta completata l'esecuzione, il Worker restituisce il risultato sotto forma di Promise. Quando il messaggio viene ricevuto dal thread principale, il risultato viene utilizzato per aggiornare i grafici. E così via per diverse iterazioni.

E per quanto riguarda le prestazioni 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 è incluso, poiché in entrambe le implementazioni viene utilizzato JavaScript.

Utilizzando la soluzione "pronta all'uso", l'aumento delle prestazioni è stato di nove volte.

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

È un ottimo risultato, ma, come si è scoperto, c'è spazio per ulteriori ottimizzazioni. Infatti, un gran numero di risultati dell'analisi QC non utilizza seqtk, quindi possono essere rimossi. Se questo viene fatto, il risultato rispetto a JS migliora di 13 volte.

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

Si è riusciti a ottenere ciò semplicemente commentando i comandi printf().

Ma non è tutto. Infatti, a questo punto fastq.bio ottiene i risultati dell'analisi mediante la chiamata di diverse funzioni C. Ognuna di esse calcola il proprio insieme di caratteristiche, quindi ogni frammento del file veniva letto due volte.

Per risolvere questo problema, è stata decisa la combinazione di due funzioni in una. Di conseguenza, le prestazioni sono aumentate di 20 volte.

Come abbiamo accelerato un'app web di 20 volte con WebAssembly

È importante notare che un risultato così eccezionale non può essere raggiunto sempre. In alcuni casi, le prestazioni possono diminuire, quindi è opportuno valutare ciascun caso specifico.

In conclusione, si può affermare che Wasm offre davvero l'opportunità di migliorare le prestazioni dell'applicazione, ma va usato con saggezza.

Skillbox consiglia:

Fonte: habr.com

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