O suporte para thread do WebAssembly foi lançado no Chrome 70 em um teste de origem.
O WebAssembly (Wasm) permite a compilação de códigos escritos em C++ e outras linguagens para execução na Web. Um recurso muito útil dos aplicativos nativos é a capacidade de usar linhas de execução, que são um primitivo para computação paralela. A maioria dos desenvolvedores em C e C++ conhece o pthreads, que é uma API padronizada para gerenciamento de linhas de execução em um aplicativo.
O grupo da comunidade WebAssembly está trabalhando para disponibilizar as conversas na Web a fim de permitir aplicativos reais com várias linhas de execução. Como parte desse esforço, o V8 implementou o suporte necessário para linhas de execução no mecanismo WebAssembly, disponível em um teste de origem. Os testes de origem permitem que os desenvolvedores testem novos recursos da Web antes de serem totalmente padronizados. Isso nos permite coletar feedback do mundo real de desenvolvedores intrépidos, o que é fundamental para validar e melhorar novos recursos.
A versão do Chrome 70 é compatível com linhas de execução do WebAssembly. Incentivamos os desenvolvedores interessados a começar a usá-las e enviar feedback.
Conversas? E quanto aos workers?
Os navegadores oferecem suporte ao paralelismo por Web Workers desde 2012 no Chrome 4. Na verdade, é normal ouvir termos como "na linha de execução principal" etc. No entanto, os Web workers não compartilham dados mutáveis entre eles, em vez de depender da transmissão de mensagens para comunicação. Na verdade, o Chrome aloca um novo mecanismo V8 para cada um deles (chamado de isolamento). Os isolamentos não compartilham código compilado nem objetos JavaScript e, portanto, não podem compartilhar dados mutáveis, como pthreads.
As linhas de execução do WebAssembly, por outro lado, podem compartilhar a mesma memória do Wasm. O armazenamento da memória compartilhada é alcançado com um SharedArrayBuffer, um primitivo de JavaScript que permite compartilhar o conteúdo de um único ArrayBuffer simultaneamente entre workers. Cada linha de execução do WebAssembly é executada em um Web Worker, mas a memória Wasm compartilhada permite que elas funcionem de maneira semelhante às plataformas nativas. Isso significa que os aplicativos que usam linhas de execução Wasm são responsáveis por gerenciar o acesso à memória compartilhada como em qualquer aplicativo com linha de execução tradicional. Existem muitas bibliotecas de código escritas em C ou C++ que usam pthreads. Elas podem ser compiladas no Wasm e executadas no modo de linha de execução verdadeira, permitindo que mais núcleos trabalhem nos mesmos dados simultaneamente.
Um exemplo simples
Confira um exemplo de um programa em C simples que usa linhas de execução.
#include <pthread.h>
#include <stdio.h>
// Calculate Fibonacci numbers shared function
int fibonacci(int iterations) {
int val = 1;
int last = 0;
if (iterations == 0) {
return 0;
}
for (int i = 1; i < iterations; i++) {
int seq;
seq = val + last;
last = val;
val = seq;
}
return val;
}
// Start function for the background thread
void *bg_func(void *arg) {
int *iter = (void *)arg;
*iter = fibonacci(*iter);
return arg;
}
// Foreground thread and main entry point
int main(int argc, char *argv[]) {
int fg_val = 54;
int bg_val = 42;
pthread_t bg_thread;
// Create the background thread
if (pthread_create(&bg_thread, NULL, bg_func, &bg_val)) {
perror("Thread create failed");
return 1;
}
// Calculate on the foreground thread
fg_val = fibonacci(fg_val);
// Wait for background thread to finish
if (pthread_join(bg_thread, NULL)) {
perror("Thread join failed");
return 2;
}
// Show the result from background and foreground threads
printf("Fib(42) is %d, Fib(6 * 9) is %d\n", bg_val, fg_val);
return 0;
}
Esse código começa com a função main()
, que declara duas variáveis, fg_val
e bg_val
. Há também uma função chamada
fibonacci()
, que será chamada pelas duas
linhas de execução neste exemplo. A função main()
cria uma linha de execução em segundo plano usando pthread_create()
,
cuja tarefa é calcular o valor da sequência do número Fibonacci correspondente ao
valor da variável bg_val
. Enquanto isso, a
função main()
em execução na linha de execução de primeiro plano
a calcula para a variável fg_val
. Quando a
linha de execução em segundo plano termina a execução, os resultados são impressos.
Compilar para oferecer suporte a linhas de execução
Primeiro, você precisa ter o SDK emscripten instalado, de preferência na versão 1.38.11 ou mais recente. Para criar nosso código de exemplo com linhas de execução ativadas para execução no navegador, precisamos transmitir algumas flags extras para o compilador emscripten emcc (links em inglês). Nossa linha de comando é semelhante a esta:
emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c
O argumento de linha de comando "-s USE_PTHREADS=1
" ativa o suporte a linhas de execução para
o módulo WebAssembly compilado, e o argumento "-s PTHREAD_POOL_SIZE=2
"
informa ao compilador para gerar um pool de duas (2) linhas de execução.
Quando o programa é executado, ele carrega o módulo WebAssembly,
cria um Web Worker para cada uma das linhas de execução no pool e compartilha o módulo
com cada worker. Neste caso, ele é 2, que vai ser usado sempre que uma
chamada para pthread_create()
for feita. Cada worker instancia o
módulo Wasm com a mesma memória, permitindo que eles colaborem. As mais recentes
mudanças do V8 na versão 7.0 compartilham o código nativo compilado dos módulos Wasm que são transmitidos
entre workers, o que permite que até mesmo aplicativos muito grandes sejam escalonados para muitos
workers. Observe que faz sentido garantir que o tamanho do pool de linhas de execução seja igual ao
número máximo de linhas de execução de que seu aplicativo precisa. Caso contrário, a criação de linhas de execução pode falhar.
Ao mesmo tempo, se o tamanho do pool de linhas de execução for muito grande, você criará
Web Workers desnecessários que ficarão sem fazer nada além de usar a memória.
Como testar
A maneira mais rápida de testar nosso módulo WebAssembly é ativar o
suporte experimental às linhas de execução do WebAssembly no Chrome 70 em diante. Navegue até o URL about://flags
no navegador, conforme mostrado abaixo:
Em seguida, encontre a configuração experimental de linhas de execução do WebAssembly, que tem esta aparência:
Mude a configuração para Ativado, conforme mostrado abaixo, e reinicie o navegador.
Depois que o navegador for reiniciado, tente carregar o módulo WebAssembly em linha de execução com uma página HTML mínima, contendo apenas o seguinte conteúdo:
<!DOCTYPE html>
<html>
<title>Threads test</title>
<body>
<script src="test.js"></script>
</body>
</html>
Para testar esta página, é necessário executar alguma forma de servidor da Web e carregá-la do navegador. Isso fará com que o módulo WebAssembly seja carregado e executado. Quando você abre o DevTools, a saída da execução é mostrada, e você vê algo parecido com a imagem de saída abaixo no console:
Nosso programa WebAssembly com linhas de execução foi executado. Recomendamos que você teste seu próprio aplicativo em linha de execução usando as etapas descritas acima.
Como testar em campo com um teste de origem
Para fins de desenvolvimento, testar linhas de execução ativando flags experimentais no navegador é bom para fins de desenvolvimento. No entanto, se você quiser testar seu aplicativo em campo, faça isso com o que é conhecido como teste de origem.
Com os testes de origem, você pode testar recursos experimentais com seus usuários usando um token de teste vinculado ao seu domínio. Em seguida, você pode implantar o app e esperar que ele funcione em um navegador com suporte ao recurso que está sendo testado (neste caso, no Chrome 70 em diante). Para receber seu próprio token e executar um teste de origem, use o formulário de inscrição.
Hospedamos nosso exemplo simples acima usando um token de teste de origem para que você possa testá-lo por conta própria sem precisar criar nada.
Se você quiser ver o que quatro linhas de execução em paralelo podem fazer na arte ASCII, confira esta demonstração também.
Enviar feedback
As linhas de execução do WebAssembly são um novo primitivo extremamente útil para a portabilidade de aplicativos para a Web. Agora é possível executar aplicativos e bibliotecas em C e C++ que exigem suporte a pthreads no ambiente WebAssembly.
Queremos receber feedback dos desenvolvedores que estão testando esse recurso, porque ele vai nos ajudar a informar o processo de padronização e validar a utilidade dele. A melhor maneira de enviar feedback é informar problemas e/ou se envolver com o processo de padronização no Grupo da comunidade WebAssembly.