
Este artigo aborda um caso de aceleração de aplicações web através da substituição de cálculos em JavaScript por WebAssembly.
WebAssembly - o que é?
Em resumo, é um formato binário de instruções para máquinas virtuais stack. Muitas vezes, Wasm (abreviação) é chamado de linguagem de programação, mas isso não é verdade. O formato de instruções é executado no navegador junto com o JavaScript.
É importante destacar que o WebAssembly pode ser gerado ao compilar código fonte em linguagens como C/C++, Rust e Go. Utiliza tipagem estática e o que é conhecido como modelo de memória plano. O código, como mencionado, é armazenado em um formato binário compacto, o que permite que seja executado quase tão rapidamente quanto se a aplicação fosse iniciada através da linha de comando. Essas características contribuíram para a popularidade crescente do WebAssembly.
Lembramos: para todos os leitores do «Habr» — desconto de 10.000 rublos na inscrição em qualquer curso Skillbox com o código promocional «Habr».
A Skillbox recomenda: Curso Prático .
Atualmente, o Wasm é utilizado em muitas aplicações, desde jogos como Doom 3 até aplicativos web portados como Autocad e Figma. O Wasm também é aplicado em áreas como computação serverless.
Neste artigo, apresentamos um exemplo do uso do Wasm para acelerar um serviço web de análise. Para ilustrar, utilizamos um aplicativo funcional escrito em C, que será compilado em WebAssembly. O resultado será usado para substituir partes do JS que têm baixo desempenho.
Transformação do aplicativo
O exemplo utilizará o serviço web fastq.bio, que é destinado a geneticistas. A ferramenta permite avaliar a qualidade do sequenciamento (decodificação) de DNA.
Aqui está um exemplo do aplicativo em funcionamento:

Os detalhes do processo não precisam ser apresentados, pois são bastante complexos para não especialistas, mas, resumindo, os cientistas, com base na infografia mencionada acima, podem entender se o processo de sequenciamento de DNA ocorreu sem problemas e quais dificuldades surgiram.
Este serviço possui alternativas, programas desktop. Mas o fastq.bio permite acelerar o trabalho, visualizando os dados. Na maioria dos outros casos, é necessário saber trabalhar com a linha de comando, mas nem todos os geneticistas possuem a experiência necessária.
Tudo funciona de forma simples. Na entrada, estão os dados apresentados em um arquivo de texto. Este arquivo é gerado por ferramentas especializadas de sequenciamento. O arquivo contém uma lista de sequências de DNA e uma avaliação da qualidade de cada nucleotídeo. O formato do arquivo é .fastq, e por isso o serviço recebeu esse nome.
Implementação em JavaScript
O primeiro passo do usuário ao trabalhar com fastq.bio é escolher o arquivo apropriado. Usando o objeto File, o aplicativo lê uma amostra aleatória de dados do arquivo e processa esse pacote. A tarefa do JavaScript aqui é realizar operações simples em strings e calcular métricas. Uma delas é a contagem dos nucleotídeos A, C, G e T em diferentes fragmentos de DNA.
Após calcular as métricas necessárias, elas são visualizadas com o auxílio de Plotly.js, e o serviço começa a trabalhar com uma nova amostra de dados. A divisão em fragmentos foi feita para melhorar a qualidade da experiência do usuário (UX). Se trabalhasse com todos os dados de uma só vez, o processo travaria por um tempo, já que os arquivos de resultados de sequenciamento ocupam centenas de gigabytes de espaço em disco. O serviço, por sua vez, pega segmentos de dados de 0,5 a 1 MB e trabalha com eles passo a passo, construindo dados gráficos.
Veja como isso funciona:

No retângulo vermelho está o algoritmo de transformação de strings para gerar a visualização. Esta é a parte mais exigente em termos de computação do serviço. Seria interessante tentar substituí-lo por Wasm.
Testando WebAssembly
Para avaliar a viabilidade do uso de Wasm, a equipe do projeto começou a procurar soluções prontas para criar uma métrica de QC (QC - controle de qualidade) com base em arquivos fastq. A busca foi feita entre ferramentas escritas em C, C++ ou Rust, para que houvesse a possibilidade de portar o código para WebAssembly. Além disso, a ferramenta não deveria ser ‘crua’; era necessário um serviço já validado por cientistas.
Como resultado, a escolha recaiu sobre . O aplicativo é bastante popular, é open-source e o idioma de origem é C.
Antes de converter para Wasm, vale a pena olhar o princípio de compilação do seqtk para desktop. De acordo com o Makefile, isto é o que é necessário:
# Compile to binary
$ gcc seqtk.c
-o seqtk
-O2
-lm
-lzBasicamente, pode-se compilar o seqtk usando Emscripten. Se não tiver, contornamos .
$ docker pull robertaboukhalil/emsdk:1.38.26
$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26Se desejar, , mas isso leva tempo.
Dentro do contêiner, é possível utilizar emcc como alternativa ao gcc sem problemas:
# Compile to WebAssembly
$ emcc seqtk.c
-o seqtk.js
-O2
-lm
-s USE_ZLIB=1
-s FORCE_FILESYSTEM=1Mudanças mínimas:
Em vez de gerar um arquivo binário, o Emscripten usa .wasm e .js, que são aplicados para executar o módulo WebAssembly.
Para suportar a biblioteca zlib, utiliza-se o flag USE_ZLIB. Esta biblioteca é amplamente utilizada e portável para o WebAssembly, e o Emscripten a inclui no projeto.
A virtual file system do Emscripten é ativada. Isso , que opera na memória dentro do navegador. Quando a página é atualizada, a memória é limpa.
Para entender por que um sistema de arquivos virtual é necessário, vale a pena comparar a forma de executar seqtk a partir da linha de comando com a maneira de iniciar o módulo compilado de WebAssembly.
# On the command line
$ ./seqtk fqchk data.fastq
# In the browser console
> Module.callMain(["fqchk", "data.fastq"])O acesso ao sistema de arquivos virtual é necessário para não reescrever seqtk para entrada por string, em vez de entrada por arquivo. Nesse caso, o fragmento de dados é mapeado como um arquivo data.fastq no sistema de arquivos virtual ao chamar main() seqtk.
Aqui está a nova arquitetura:

A ilustração demonstra que, em vez de realizar cálculos na thread principal do navegador, são utilizados . Essa abordagem permite que cálculos sejam realizados em uma thread de fundo, sem degradar a responsividade do navegador. O controlador WebWorker inicia o Worker, gerenciando sua interação com a thread principal.
O comando seqtk é executado com o Worker no arquivo montado. Após a conclusão da execução, o Worker fornece o resultado em forma de Promise. Quando a mensagem é recebida pela thread principal, o resultado é utilizado para atualizar os gráficos. E assim acontece em várias iterações.
E quanto ao desempenho do WebAssembly?
Para avaliar a alteração de desempenho, a equipe do projeto utilizou a métrica de operações de leitura por segundo. O tempo para construir gráficos interativos não é considerado, pois em ambas as implementações é utilizado JavaScript.
Ao utilizar a solução "pronta para uso", o ganho de desempenho foi de nove vezes.

Esse é um excelente resultado, mas, como se verificou, há possibilidade de otimizá-lo ainda mais. O fato é que um grande número de resultados de análise de QC não é utilizado pelo seqtk, portanto, pode ser removido. Se isso for feito, o resultado em comparação ao JS melhora em 13 vezes.

Isso foi alcançado simplesmente comentando os comandos printf().
Mas isso não é tudo. Na verdade, nesse estágio, fastq.bio obtém os resultados da análise chamando diferentes funções em C. Cada uma delas calcula seu próprio conjunto de características, de modo que cada fragmento do arquivo é lido duas vezes.
Para contornar esse problema, decidiu-se combinar duas funções em uma só. Como resultado, o desempenho aumentou em 20 vezes.

Vale ressaltar que esse resultado excepcional não pode ser alcançado em todas as ocasiões. Em alguns casos, o desempenho cai, então é importante avaliar cada caso específico.
Em resumo, pode-se afirmar que Wasm realmente melhora o desempenho da aplicação, mas deve ser utilizado com sabedoria.
A Skillbox recomenda:
- Curso Prático de Dois Anos .
- Curso online .
- Curso prático de um ano .
Fonte: habr.com
