Design: WebAssembly.Instance pode recompilar um módulo já compilado?

Criado em 27 out. 2016  ·  94Comentários  ·  Fonte: WebAssembly/design

Digamos que eu tenha este código:

let descriptor = /* ... */;
let memories = Array.apply(null, {length: 1024}).map(() => new WebAssembly.Memory(descriptor));
let instance = fetch('foo.wasm')
  .then(response => response.arrayBuffer())
  .then(buffer => WebAssembly.compile(buffer))
  .then(module => new WebAssembly.Instance(module, { memory: memories[1023] }));

WebAssembly.Instance permissão para bloquear por um período de tempo substancial. Poderia, por exemplo, recompilar o WebAssembly.Module ?

Na maioria dos casos, eu diria não, mas e se o código já compilado não gostar da memória que recebe? Digamos, porque essa memória é uma memória de modo lento e o código foi compilado assumindo o modo rápido? talvez memories[0] fosse uma memória de modo rápido, mas memories[1023] certamente não seria.

Que tal este código em vez disso:

let instances = [0,1,2,3,4,5,6,7].map(v => fetch(`foo${v}.wasm`)
  .then(response => response.arrayBuffer())
  .then(buffer => WebAssembly.compile(buffer))
  .then(module => new WebAssembly.Instance(module)));

Essas chamadas para WebAssembly.Instance podem causar recompilação?

Supondo que o acima faça sentido, aqui estão algumas questões relacionadas:

  • Queremos uma função assíncrona de retorno de promessa que pode compilar _and_ instanciar? Não estou dizendo que devemos descartar qualquer uma das APIs síncronas e assíncronas que já temos, estou propondo uma nova API assíncrona.
  • Como um navegador expõe que o código compilado em um WebAssembly.Module é rápido e que uma instância de WebAssembly.Memory é adequada para um código tão rápido? No momento, a resposta parece ser "experimente e veja se consegue perceber".
  • Como um usuário sabe quantas WebAssembly.Memory instâncias são permitidas antes de obter código lento (contando os implícitos, por exemplo, conforme criado pelo segundo exemplo)?
JS embedding

Comentários muito úteis

@kgryte Eu deveria ter esclarecido que meu comentário se referia principalmente ao navegador como um contexto de execução. Nós pousamos em uma superfície de API que ainda expõe as APIs síncronas. Os navegadores podem impor um limite de tamanho aos módulos passados ​​para as APIs síncronas (o Chrome já faz, por exemplo), mas esse limite é configurável pelo incorporador e não deve ser aplicado ao Node.

Todos 94 comentários

Seria bom que WebAssembly.Instance às vezes causasse recompilação, desta forma vars globais imutáveis ​​poderiam ser dobrados constantemente no código gerado. Por exemplo, o Emscripten gera código relocável, deslocando todos os ponteiros para dados estáticos. O deslocamento é passado como uma var global imutável quando o módulo é instanciado. Se WebAssembly.Instance pode recompilar, ele pode especializar o código gerado.

A especificação não define o que é "compilação", nem faria
sentido para isso, porque as abordagens de implementação podem diferir radicalmente
(incluindo intérpretes). Portanto, não pode haver qualquer palavra normativa sobre este
de qualquer jeito. O melhor que podemos fazer é adicionar uma nota que
WebAssembly.Instance deve ser "rápido".

Em 27 de outubro de 2016 às 03:24, Michael Bebenita [email protected]
escreveu:

Seria bom que WebAssembly.Instance às vezes causasse recompilação,
desta forma, vars globais imutáveis ​​podem ser dobrados constantemente no
código. Por exemplo, o Emscripten gera código relocável compensando todos
ponteiros para dados estáticos. O deslocamento é passado como uma var global imutável
quando o módulo é instanciado. Se WebAssembly.Instance puder recompilar,
ele poderia especializar o código gerado.

-
Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/WebAssembly/design/issues/838#issuecomment -256522163,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AEDOO9sJPgujK3k0f6P7laYV_zaJxES5ks5q3_1LgaJpZM4Kh1gM
.

Concordo que isso seria, no máximo, uma nota não normativa.

No SM, também pretendemos que a instanciação nunca seja recompilada para que haja um modelo de custo de compilação previsível para os desenvolvedores (em particular, para que os desenvolvedores possam usar WebAssembly.compile e o IDB para controlar quando recebem o resultado da compilação) . A recompilação do tempo de instanciação de dentro do construtor síncrono Instance certamente quebraria esse modelo de custo e poderia levar a um grande problema.

Mas eu aprecio que a compilação separada está fundamentalmente em desacordo com uma variedade de otimizações que alguém pode querer fazer para especializar o código gerado em parâmetros ambientais. Combinar compilação e instanciação em uma operação assíncrona faz sentido e é algo que consideramos no passado. A desvantagem, é claro, é que isso inibe o cache explícito (não há Module ), então o desenvolvedor tem que fazer uma troca desagradável. Algumas opções:

  • O impl pode fazer cache de conteúdo endereçado implícito (que pode incluir parâmetros de ambiente na chave), como fazemos com asm.js atualmente no FF. Isso seria uma espécie de dor e tem todos os problemas de previsibilidade / heurística de qualquer cache implícito.
  • Poderíamos criar uma nova maneira (por exemplo, uma nova API WebAssembly.Cache onde você passa bytecode e parâmetros de instanciação e recebe de volta Promise<Instance> .

Este último me intriga e poderia fornecer uma experiência de desenvolvedor muito mais agradável do que usar o BID e talvez uma chance de otimizar ainda mais o cache (já que o cache é especializado para fins), mas é certamente um grande recurso e algo que gostaríamos de levar algum tempo considerar.

@rossberg-chromium Parece que expliquei mal o meu propósito: não quero discutir sobre o que as especificações dizem. Estou tentando apontar o que parece ser uma surpresa séria para os desenvolvedores, escondendo-se sob a API. Um desenvolvedor não espera que o resultado de .compile seja recompilado. Isso parece uma falha de design para mim.

@lukewagner, mesmo com cache implícito ou explícito, podemos ter o mesmo problema: quantos WebAssembly.Memory podem ser criados no mesmo espaço de endereço / origem é uma limitação do navegador. Eu gosto do que você está sugerindo, mas acho que é ortogonal ao problema. Deixe-me saber se eu entendi mal o que você sugere.

Talvez .compile e Module possam receber um Memory , e Instance tem uma propriedade .memory que pode ser passada para outras compilações / instanciações ?

Não estou tentando eliminar a possibilidade de recompilação, acho que preferimos um uso idiomático de API comum que tenha informações perfeitas escritas Memory no momento da primeira compilação (ou no momento da recuperação do cache). se a compilação emite verificações de limites ou não, se necessário.

@jfbastien Com o cache implícito / explícito que foi fornecido com os parâmetros de instanciação específicos (então Memory ), não vejo como haveria necessidade de recompilação.

@jfbastien Com o cache implícito / explícito que foi fornecido com os parâmetros de instanciação específicos (então Memory ), não vejo como haveria necessidade de recompilação.

Pode ser:

  1. Crie muitos Memory s.
  2. Compile o código, com verificação de limites explícita (lenta) porque havia muitos Memory ies.
  3. Armazene esse código em cache.
  4. Deixar página.
  5. Carregue a página novamente.
  6. Aloque apenas um Memory , que obtém a versão rápida.
  7. Obtenha do cache.
  8. Receba o código lento Instance .

Neste ponto, eu concordo que você não _precisa_ recompilação, mas estamos sendo um pouco tolos por fazer verificações de limites lentas quando não precisamos.

Como eu disse: gosto dessa Cache API que você propõe, acho que torna o WebAssembly mais utilizável, mas acho que o problema ainda está lá. 😢

Bem, esse é o meu ponto sobre ter um cache aprimorado que aceita parâmetros de instanciação e bytecode: o cache está livre para recompilar se o que foi armazenado em cache não corresponder aos parâmetros de instanciação. Portanto, as etapas seriam apenas:

  1. crie muitos Memory s
  2. solicite um Instance do cache, passando um dos (lentos) Memory s
  3. o código lento é compilado, armazenado em cache e retornado como um Instance
  4. deixar página
  5. carregar a página novamente
  6. alocar apenas um Memory
  7. solicite um Instance do cache, passando o Memory rápido
  8. o código rápido é compilado, armazenado em cache e retornado como um Instance

e após a etapa 8, todos os carregamentos de página futuros serão armazenados em cache rápido ou código lento.

@lukewagner Em primeiro lugar, você está propondo uma mitigação que vai contra o objetivo declarado de WebAssembly fornecer desempenho determinístico. A diferença entre lento e rápido foi citado pela última vez em cerca de 20%, então seria realmente horrível se uma especificação que visa meticulosamente o desempenho determinístico o deixasse cair no chão por causa de uma peculiaridade da API. Eu não acredito que o navegador com um cache endereçado ao conteúdo seja a solução certa, porque a especificação já tem muitos problemas em outro lugar para evitar a necessidade de otimizações de cache de recompilação de perfil. Por exemplo, prometemos a compilação precisamente para que o aplicativo possa obter um comportamento razoável, mesmo se o código não estiver armazenado em cache. Se a maneira como isso é especificado exige que todos nós implementemos caches ou outras atenuações, teremos falhado em nosso objetivo de fornecer às pessoas um modelo de custo razoavelmente portátil.

Para mim, a questão é apenas esta: uma das otimizações que todos nós efetivamente teremos que fazer por razões competitivas (a verificação de limites de memória virtual de 4 GB, que chamarei apenas de hack de 4 GB) não pode ser feita na especificação atual sem sacrificar uma dessas coisas:

  • Você pode se safar se sempre alocar 4 GB de memória virtual para qualquer memória wasm. Isso desencorajará as pessoas a usar o WebAssembly para módulos pequenos, porque você atingirá os limites de alocação de memória virtual, a fragmentação da memória virtual ou outros problemas se alocar muitos deles. Também temo que, se você permitir a alocação de muitos deles, reduzirá a eficácia de atenuações de segurança como o ASLR. Observe que as APIs existentes não compartilham esse perigo, pois comprometem a memória que alocam e OOM ou travarão antes de permitir que você aloque muito mais do que a memória física permite.
  • Você pode se safar se permitir uma recompilação quando encontrar uma incompatibilidade durante a instanciação (o código compilado quer um hack de 4 GB, mas a memória não tem aquela alocação de memória virtual). Você também poderia se safar se a instanciação movesse a memória para uma região de 4 GB, mas veja o ponto anterior. Portanto, provavelmente sempre que isso acontecer, será um bug P1 para o navegador que o encontrou.

Eu acho que isso significa que a especificação irá encorajar os fornecedores a convergir para apenas permitir reservas de 4 GB sempre que a memória wasm for alocada, ou ter otimizações de cache / compilação lenta / perfil para detectar isso.

Finalmente, eu não entendo o ponto de tornar nada disso não normativo. Isso pode ser normativo, pois poderíamos fazer com que a API excluísse a possibilidade de o navegador ter que compilar algo sem saber que tipo de memória ele terá. Imagino que haja muitas maneiras de fazer isso. Por exemplo, a instanciação pode retornar uma promessa e podemos remover a etapa de compilação separada. Isso deixaria claro que a instanciação é a etapa que pode demorar, o que implica fortemente para o cliente que esta é a etapa que faz a compilação. Em tal API, o compilador sempre sabe se a memória para a qual está compilando tem o hack de 4 GB ou não.

É triste que estejamos apenas percebendo isso agora, mas estou surpreso que vocês não percebam que este é um problema maior. Existe alguma atenuação além do cache que estou esquecendo?

@jfbastien em seu cenário motivador, você apontou que o módulo foi criado para dar preferência à memória rápida. Estou assumindo que você está buscando principalmente habilitar a otimização rápida de memória quando um módulo em particular deseja, e pode estar OK em não fazer isso quando o módulo não quiser (nada de ruim em dar de cara com ela de forma oportunista, nesse caso, também , apenas tentando separar as prioridades).

Em caso afirmativo, como seriam essas alternativas de armazenamento em cache ou instanciar assíncrona:

  1. O autor do módulo deve exigir 4 GB de memória mínima / máxima
  2. Uma variante de compilação (pelo menos async, talvez também sync) que produz uma instância que aceita apenas memória rápida.

Para o problema do "hack de 4 GB" e incompatibilidades entre a memória que o usa e o código que o espera, faria sentido para a compilação emitir internamente duas versões do código? (Obviamente, isso usaria mais memória, o que é triste, mas espero que o tempo de compilação não seja muito pior, o escritor poderia gerar os dois ao mesmo tempo?)

@mtrofin Não acho que faça sentido pedir 4GiB se você não pretende usá-lo. A alocação virtual é separada da intenção de uso, então acho que precisaríamos separar as duas.

Em 2: ainda não ajuda muito o desenvolvedor: se eles usarem essa variante e ela falhar, e daí?

@kripken Não acho que a compilação dupla seja uma boa ideia.

@kripken Acho que é isso que faríamos sem qualquer outra resolução para este problema.

Quero que o WebAssembly seja ótimo no caso de navegação casual: você me fala sobre uma coisa legal, me manda a URL, eu clico e me divirto por alguns minutos. Isso é o que torna a web legal. Mas isso significa que muitas compilações serão de código que não está armazenado em cache, então o tempo de compilação terá um grande papel na vida da bateria do usuário. Então, a compilação dupla me deixa triste.

@mtrofin

O autor do módulo deve exigir 4 GB de memória mínima / máxima

Isso não é muito prático, já que muitos dispositivos não têm 4 GB de memória física. Além disso, isso é difícil de especificar.

Uma variante de compilação (pelo menos async, talvez também sync) que produz uma instância que aceita apenas memória rápida.

Não acho que queremos compilações duplas.

@pizlonator Até agora, não consideramos designs que exigiam diferentes modos de codegen: apenas sempre alocamos regiões de 4 gb em 64 bits e observamos que isso funcionou para muitos milhares de memórias no Linux, OSX e Windows. Temos um limite superior conservador para evitar o esgotamento total trivial do espaço de endereço disponível, o que eu espero que seja suficiente para suportar o caso de uso de muitas bibliotecas pequenas. Portanto, acho que a nova restrição que estamos abordando aqui é que o iOS tem algumas limitações de espaço de endereço virtual que podem reduzir o número de alocações de 4 GB.

Portanto, uma observação é que uma grande parte da eliminação de verificação de limites permitida pelo hack de 4 GB pode ser evitada tendo apenas uma pequena região de guarda no final da memória wasm. Nossos experimentos iniciais mostram que análises básicas (nada a ver com loops, apenas eliminando verificações em carregamentos / armazenamentos com o mesmo ponteiro de base) já podem eliminar aproximadamente a metade das verificações de limites. E provavelmente isso poderia melhorar. Portanto, o hack de 4 GB seria uma aceleração mais modesta e menos necessária.

Outra ideia que tive antes seria compilar pessimisticamente o código com verificações de limites (usando a eliminação com base na página de proteção) e, em seguida, removê-los ao instanciar com uma memória de modo rápido. Combinados, a sobrecarga pode ser bem pequena em comparação com o código de modo rápido idealizado.

@lukewagner

Até agora, não consideramos designs que exigiam diferentes modos de codegen: apenas sempre alocamos regiões de 4 gb em 64 bits e observamos que isso funcionou para muitos milhares de memórias no Linux, OSX e Windows. Temos um número total conservador para evitar o esgotamento total trivial do espaço de endereço disponível, o que eu espero que seja suficiente para suportar o caso de uso de muitas bibliotecas pequenas. Portanto, acho que a nova restrição que estamos abordando aqui é que o iOS tem algumas limitações de espaço de endereço virtual que podem reduzir o número de alocações de 4 GB.

Este não é um problema específico do iOS. O problema é que, se você permitir muitas dessas alocações, isso representará um risco de segurança, porque cada uma dessas alocações reduz a eficácia do ASLR. Portanto, acho que a VM deve ter a opção de definir um limite muito baixo para o número de espaços de 4 GB que aloca, mas isso implica que o caminho de fallback não deve ser muito caro (ou seja, não deve exigir recompilação).

Qual limite você tem para o número de memórias de 4 GB que você alocaria? O que você faz quando atinge esse limite - desiste totalmente ou recompila na instanciação?

Portanto, uma observação é que uma grande parte da eliminação de verificação de limites permitida pelo hack de 4 GB pode ser evitada tendo apenas uma pequena região de guarda no final da memória wasm. Nossos experimentos iniciais mostram que análises básicas (nada a ver com loops, apenas eliminando verificações em carregamentos / armazenamentos com o mesmo ponteiro de base) já podem eliminar aproximadamente a metade das verificações de limites. E provavelmente isso poderia melhorar. Portanto, o hack de 4 GB seria uma aceleração mais modesta e menos necessária.

Concordo que a análise nos permite eliminar mais verificações, mas o hack de 4 GB é o caminho a percorrer se você quiser o desempenho máximo. Todo mundo quer o máximo de desempenho, e acho que seria ótimo tornar possível obter o máximo de desempenho sem causar problemas de segurança, de recursos e recompilações inesperadas.

Outra ideia que tive antes seria compilar pessimisticamente o código com verificações de limites (usando a eliminação com base na página de proteção) e, em seguida, removê-los ao instanciar com uma memória de modo rápido. Combinados, a sobrecarga pode ser bem pequena em comparação com o código de modo rápido idealizado.

O código que tem verificações de limites é melhor fixando um registro para o tamanho da memória e fixando um registro para a base de memória.

O código que usa o hack de 4 GB precisa apenas fixar um registro para base de memória.

Portanto, esta não é uma ótima solução.

Além do aborrecimento de ter que disputar as especificações e implementações, quais são as desvantagens de combinar compilação e instanciação em uma ação prometida?

O problema é que, se você permitir muitas dessas alocações, isso representa um risco de segurança porque cada uma dessas
alocação reduz a eficácia do ASLR.

Não sou um especialista em ASLR, mas iiuc, mesmo se não tivéssemos um limite conservador (isto é, se permitíssemos que você continuasse alocando até mmap falhou porque o kernel atingiu seu número de máximo de intervalos de endereço), apenas uma pequena fração de todo o espaço endereçável de 47 bits seria consumida, de modo que a colocação do código continuaria a ser altamente aleatória neste espaço de 47 bits. O posicionamento do código IIUC, ASLR também não é completamente aleatório; apenas o suficiente para tornar difícil prever onde qualquer coisa estará.

Qual limite você tem para o número de memórias de 4 GB que você alocaria? O que você faz
quando você atinge esse limite - desistir totalmente ou recompilar na instanciação?

Bem, uma vez que é do asm.js dias, apenas 1000. Então a alocação de memória apenas joga. Talvez precisemos superar isso, mas mesmo com muitos aplicativos supermodularizados (com muitos módulos seprate wasm cada) compartilhando o mesmo processo, não posso imaginar que precisaríamos de muito mais. Acho que Memory é diferente do velho ArrayBuffer s porque os aplicativos não vão querer criar milhares.

Além do aborrecimento de ter que discutir as especificações e implementações, quais são as desvantagens
de combinar compilação e instanciação em uma ação prometida?

Como mencionei acima, adicionar uma API Promise<Instance> eval(bytecode, importObj) é bom, mas agora coloca o desenvolvedor em uma posição difícil porque agora ele tem que escolher entre um aumento de desempenho em algumas plataformas ou ser capaz de armazenar em cache seu código compilado em todas as plataformas. Parece que precisamos de uma solução que se integre ao cache e é isso que eu estava pensando acima com a API Cache explícita.

Nova ideia: e se adicionarmos uma versão assíncrona de new Instance , digamos WebAssembly.instantiate e, como com WebAssembly.compile , dissermos que todos devem usar a versão assíncrona? Isso é algo que venho considerando _de qualquer forma_, uma vez que a instanciação pode levar alguns ms se patch for usado. Então dizemos na especificação que o motor pode fazer trabalhos caros em compile ou instantiate (ou nenhum, se um motor faz validação / compilação preguiçosa!).

Isso ainda deixa a dúvida sobre o que fazer quando um compile d Module é armazenado no BID, mas essa é apenas uma questão difícil quando há vários modos codegen _ qualquer maneira_. Uma ideia é que Module s que são armazenados ou recuperados do BID seguram um identificador para sua entrada no BID e adicionam um novo código compilado a essa entrada. Dessa forma, a entrada do BID acumularia preguiçosamente uma ou mais versões compiladas de seu módulo e seria capaz de fornecer o que fosse necessário durante a instanciação.

A parte do BID é um pouco mais trabalhosa, mas parece muito próxima do ideal em termos de desempenho. WDYT?

Eu acho que adicionar assíncrono instantiate faz sentido, mas eu também adicionaria um parâmetro Memory a compile . Se passar uma memória diferente para instantiate , você pode ser recompilado, caso contrário, você já "vinculou" a memória ao compilar.

Eu não pensei sobre o cache o suficiente para ter uma opinião totalmente formada ainda.

@lukewagner

Não sou um especialista em ASLR, mas, iiuc, mesmo se não tivéssemos um limite conservador (ou seja, se permitíssemos que você continuasse alocando até que o mmap falhou porque o kernel atingiu seu número máximo de intervalos de endereços) , apenas uma pequena fração de todo o espaço endereçável de 47 bits seria consumida, de modo que a colocação do código continuaria a ser altamente aleatória nesse espaço de 47 bits. O posicionamento do código IIUC, ASLR também não é completamente aleatório; apenas o suficiente para tornar difícil prever onde qualquer coisa estará.

ASLR afeta o código e os dados. O objetivo é tornar mais caro para um invasor se infiltrar em uma estrutura de dados sem perseguir um ponteiro para ela. Se o invasor puder exaurir a memória, ele definitivamente terá mais vantagem.

Bem, uma vez que é do asm.js dias, apenas 1000. Então a alocação de memória apenas joga. Talvez precisemos superar isso, mas mesmo com muitos aplicativos supermodularizados (com muitos módulos seprate wasm cada) compartilhando o mesmo processo, não posso imaginar que precisaríamos de muito mais. Eu acho que o Memory é diferente dos velhos ArrayBuffers porque os aplicativos não vão querer criar milhares.

1000 parece um limite razoável. Vou perguntar por aí com o pessoal da segurança.

Como mencionei acima, adicionar uma promessaeval (bytecode, importObj) API é bom, mas agora coloca o desenvolvedor em uma posição difícil porque agora ele tem que escolher entre um aumento de desempenho em algumas plataformas vs. ser capaz de armazenar em cache seu código compilado em todas as plataformas. Parece que precisamos de uma solução que se integre ao cache e é isso que eu estava pensando acima com a API de cache explícita.

Direito. Posso ver algumas maneiras de fazer essa API funcionar. Uma API cafona, mas prática, seria sobrecarregar a avaliação:

  1. instancePromise = eval (bytecode, importObj)
  2. instancePromise = eval (módulo, importObj)

e a instância tem um getter:

module = instance.module

Onde o módulo é clonável por estrutura.

O que você acha disso?

Nova ideia: e se adicionarmos uma versão assíncrona de uma nova instância, digamos WebAssembly.instantiate e, como acontece com WebAssembly.compile, dissermos que todos devem usar a versão assíncrona? Isso é algo que venho considerando de qualquer maneira, uma vez que a instanciação pode levar alguns ms se o patch for usado. Em seguida, dizemos na especificação que o mecanismo pode fazer um trabalho caro tanto na compilação quanto na instanciação (ou nenhum dos dois, se um mecanismo fizer validação / compilação preguiçosa!).

Isso ainda deixa a questão do que fazer quando um Módulo compilado é armazenado no IDB, mas essa é apenas uma questão difícil quando há vários modos codegen de qualquer maneira. Uma ideia é que os Módulos que são armazenados ou recuperados do BID seguram um identificador para sua entrada no BID e adicionam um novo código compilado a essa entrada. Dessa forma, a entrada do BID acumularia preguiçosamente uma ou mais versões compiladas de seu módulo e seria capaz de fornecer o que fosse necessário durante a instanciação.

A parte do BID é um pouco mais trabalhosa, mas parece muito próxima do ideal em termos de desempenho. WDYT?

Intrigante. Em relação à minha ideia acima:

Pro: a sua é uma abstração fácil de entender que é conceitualmente semelhante ao que dizemos agora.
Con: o seu não leva a tanta sinergia entre o que o usuário faz e o que o motor faz como minha proposta permite.

Existem três áreas em que sua proposta não dá ao usuário tanto controle quanto a minha:

  1. O trabalho caro pode acontecer em um de dois lugares, então o usuário tem que se planejar para qualquer um deles sendo caro. Provavelmente teremos conteúdo da web que se comportará mal se um deles for caro, porque foi ajustado para casos em que era barato. Minha proposta tem um lugar onde coisas caras acontecem, levando a mais uniformidade entre as implementações.
  2. Não há um caminho claramente garantido para que todas as versões do código compilado sejam armazenadas em cache. Por outro lado, meu uso de encadear o módulo por meio da API significa que a VM pode construir o módulo com mais coisas a cada vez, enquanto ainda permite que o usuário gerencie o cache. Portanto, se na primeira vez fizermos 4 GB, é isso que vamos armazenar em cache, mas se não conseguirmos fazer 4 GB na segunda vez, poderemos potencialmente armazenar em cache ambos (se o usuário armazenar em cache instance.module após cada compilação).
  3. Casos incomuns no navegador ou outros problemas às vezes podem levar a uma dupla compilação em seu esquema, porque compilaríamos uma coisa na etapa de compilação, mas depois perceberíamos que precisamos de outra coisa na etapa de instanciação. Minha versão nunca requer uma compilação dupla.

Então, eu gosto mais do meu. Dito isso, acho que sua proposta é uma progressão, então definitivamente me parece bom.

Esse problema depende da frequência com que a fragmentação torna a alocação rápida de
a memória (aliás, você terá 4 GB + deslocamento máximo suportado, ou 8 GB) falha. Se o
provavelmente é bem inferior a 1%, então pode não ser totalmente irracional
que seja uma situação OOM.

No cenário em que o usuário está navegando na web e usando muitos
pequenos módulos WASM em rápida sucessão, presumivelmente, eles não estão todos disponíveis
uma vez. Nesse caso, um pequeno cache de pedaços reservados de 4 GB atenuaria o
edição.

Outra estratégia possível é gerar uma versão do código com
verificações de limites e, se houver memória rápida disponível, basta sobrescrever os limites
verifica com nops. Isso é feio, mas é muito mais rápido do que um
recompilar e menos espaço do que duas compilações.

Na quinta-feira, 27 de outubro de 2016 às 21h03, pizlonator [email protected]
escreveu:

@mtrofin https://github.com/mtrofin

O autor do módulo deve exigir 4 GB de memória mínima / máxima

Isso não é muito prático, já que muitos dispositivos não têm 4 GB de
memória. Além disso, isso é difícil de especificar.

Uma variante de compilar (assíncrona, pelo menos, talvez também sincronizar) que produz um
instância aceitando apenas memória rápida.

Não acho que queremos compilações duplas.

-
Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/WebAssembly/design/issues/838#issuecomment -256738329,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/ALnq1F6CYUaq0unla0H6RYivUC8jfxIAks5q4PWIgaJpZM4Kh1gM
.

Não é apenas ASLR: é também poluição de pagetable / alocador / etc. Todos nós precisamos conversar com nosso pessoal de segurança _tanto como_ nosso pessoal de kernel / sistemas. Ou podemos ser diretos aos desenvolvedores sobre os limites que cada mecanismo impõe ao Memory "rápido" e torná-lo idiomático na API para que seja difícil usá-lo incorretamente.

Existem todas essas muletas que podemos usar, como nop ou compilação dupla, mas por que ter muletas?

@jfbastien Não creio que a memória do PROT_NONE custe as entradas da tabela de páginas; Acho que há uma estrutura de dados separada que contém mapeamentos dos quais a tabela de páginas é preenchida lentamente.

@pizlonator Eu gosto dessa ideia, e posso ver que é o que encorajamos todos a usarem por padrão em tutoriais, conjunto de ferramentas, etc. Também é mais sucinto e fácil de ensinar se você puder simplesmente ignorar Module . Isso também pode resolver a preocupação de @ s3ththompson sobre desencorajar o uso das APIs de sincronização, tornando a API mais agradável a assíncrona.

No entanto, acho que não devemos tirar WebAssembly.compile e o construtor Module : estou imaginando cenários onde você tem um "servidor de código" (fornecendo cache de código de origem cruzada via BID + postMessage ; isso já foi discutido especificamente com alguns usuários) que deseja compilar e armazenar em cache o código sem ter que "falsificar" os parâmetros de instanciação. (Também pode haver alguma sobrecarga desnecessária (lixo, patching, etc) para uma instanciação desnecessária.) E, para os mesmos casos que desejam compilação síncrona (via new Module ), precisaríamos manter new Instance .

Então, se concordarmos com isso, o que isso se resumirá a uma proposta puramente aditiva das duas sobrecargas de WebAssembly.eval você mencionou. Sim?

No entanto, um ajuste: acho que não devemos ter um getter module pois isso exigiria o Instance para manter alguns dados internos (viz., Bytecode) durante a vida útil do Instance ; agora Module geralmente pode ser GCed imediatamente após a instanciação. Isso sugere uma propriedade de dados (que o usuário pode remover, embora provavelmente se esqueça) ou talvez uma terceira versão de eval que retorna um par {instance, module} ...

Ter uma API assíncrona de uma etapa como o caso recomendado para o aplicativo monolítico típico faz sentido como o padrão recomendado.

Concordou com @lukewagner que tanto o caso all sync (compilação inline) coberto pelo novo Módulo + nova Instância é útil.
Além disso, o servidor de compilação de plano de fundo (assíncrono) com instância de sincronização também parece útil.

Adicionar as duas variantes de avaliação propostas parece uma boa maneira de introduzir isso.

No entanto, não gosto do nome, porque será confundido na mente das pessoas (de segurança) com js eval (que se assemelha de uma maneira, mas não em termos de captura de escopo).
Que tal WebAssembly.instantiate?

Hah, bom ponto, eval tem um pouco de reputação . +1 para WebAssembly.instantiate .

Qual seria a orientação para o desenvolvedor quando usar a instância assíncrona?

@mtrofin Para usar WebAssembly.instantiate por padrão, a menos que eles tivessem algum esquema especial de compartilhamento / carregamento de código que exigisse a compilação de Module s independentemente de qualquer uso em particular.

@lukewagner Isso parece razoável.

Hah, bom ponto, eval tem um pouco de reputação. +1 para WebAssembly.instantiate.

Concordou.

Portanto, se concordarmos com isso, o que isso se resumirá a uma proposta puramente aditiva das duas sobrecargas WebAssembly.eval que você mencionou. Sim?

Isso é o que parece.

Acho que não deveríamos ter um getter de módulo, pois isso exigiria que a Instância mantivesse alguns dados internos (viz., Bytecode) durante o tempo de vida da Instância; agora, geralmente, o módulo pode ser submetido ao GC imediatamente após a instanciação. Isso sugere uma propriedade de dados (que o usuário pode remover, embora provavelmente se esqueça) ou talvez uma terceira versão de eval que retorna um par de {instância, módulo} ...

Com certeza parece que uma propriedade de dados é melhor. Ou fazer com que WebAssembly.instantiate sempre retorne uma instância, par de módulo.

Está correto? Suponha que você WebAssembly.instantiate com o objetivo de obter uma variante do módulo de memória rápida. Agora você obtém o módulo e clona-o da estrutura. Agora, este módulo precisa ser instanciado com Memory -es suportando memória rápida.

@pizlonator Sim, eu posso pedalar na minha cabeça de várias maneiras. Acho que gosto de devolver o par um pouco melhor, pois provavelmente fará com que menos pessoas arrastem acidentalmente Module .

A recompilação do @mtrofin ainda pode ser necessária quando você arranca Module de uma chamada instantiate e instantiate com novas importações; Acho que o objetivo dessa adição de API é que não será o caso comum e só acontecerá quando for fundamentalmente necessário (ou seja, você tem 1 módulo acessando dois tipos de memórias).

Este tópico está ficando longo, parece que está convergindo, mas para ter 100% de certeza, precisamos escrever o código que esperamos que diferentes usuários escrevam:

  1. Instanciação assíncrona de um único módulo.
  2. Instanciação assíncrona de um módulo, com compartilhamento de memória com outros módulos.
  3. Instanciação síncrona de um único módulo (não acho que o multimódulo síncrono seja útil?).
  4. Cache para todos eles (tanto colocando no cache, quanto recuperando e instanciando, com memória).
  5. Atualização de um único módulo .wasm e carregamentos em cache dos outros módulos.

Algo mais? Parece que @lukewagner tem ideias sobre importações que eu não groco totalmente.

Isso significa que os usos subsequentes deste módulo devem instanciar de forma assíncrona, ou arriscar bloquear o thread de UI com uma instanciação síncrona surpreendentemente longa.

@jfbastien Eu gostaria de entender para cada fragmento que esperamos que os desenvolvedores escrevam, o que os motivaria a seguir esse caminho específico e quais informações o desenvolvedor deve ter disponíveis para tomar uma decisão.

@mtrofin Certo, dado um Module m , você chamaria WebAssembly.instantiate(m) que é assíncrono. Você _pode_ chamar new Instance(m) e pode ser caro, mas não é diferente de new Module(m) .

@jfbastien Supondo que quando você diz "instanciação assíncrona", você quer dizer "compilação e instanciação assíncrona", aqui está a versão curta:

  1. WebAssembly.instantiate(bytecode, imports)
  2. WebAssembly.instantiate(bytecode, imports) , onde imports inclui a memória compartilhada
  3. new Instance(new Module(bytecode), imports)
  4. Em todos os casos, você pode obter um Module , então você put que em um IDBObjectStore . Mais tarde, você get a Module m volta e chama WebAssembly.instantiate(m, imports) .
  5. Nada realmente especial aqui: você WebAssembly.instantiate um módulo do bytecode e instantiate o resto dos Module s retirados do BID.

Devemos recomendar o uso da instância de sincronização se você achar que pode usar a compilação de sincronização e a instância de async se achar que deve usar a compilação async?

Além disso, estou preocupado que o desenvolvedor enfrente agora um sistema mais complexo: mais escolhas que transparecem otimizações que estamos planejando fazer, e não tenho certeza se o desenvolvedor tem as informações corretas disponíveis para fazer as compensações. Pensando da perspectiva do desenvolvedor, há um conjunto menor de preocupações com as quais eles se importam e se sentiriam confortáveis ​​em expressar? Falamos em um ponto sobre os desenvolvedores tendo uma "otimização às custas de pontos de falha precisos" (isso era re. Verificações de limites de içamento). Uma alternativa seria um sinalizador "otimizar"?

@mtrofin 99% do que os desenvolvedores escreveriam (ou gerariam para eles pelo conjunto de ferramentas) seria WebAssembly.instantiate . Você só usaria APIs de sincronização para "Estou escrevendo um JIT no wasm" especial e WebAssembly.compile se estivesse escrevendo algum sistema de compartilhamento de código, então acho que os tutoriais de "Introdução" cobririam exclusivamente WebAssembly.instantiate .

@lukewagner Percebi que você adicionou importações ao # 3 novo Módulo () acima. Acho que, além de adicioná-lo ao WebAssembly.compile, é uma boa ideia e completa as possibilidades.
Dessa forma, se você quiser dar uma dica sobre a memória em tempo de compilação, pode.
Se posteriormente você instanciar novamente com importações diferentes, especialmente de forma síncrona, poderá ocorrer um soluço.

Portanto, um resumo das alterações (só para ficar claro):

  • Adicionar WebAssembly.instantiate (bytes, importações) retorna a promessa de {instância :, módulo:}
  • Adicionar WebAssembly.instantiate (módulo, importações) retorna a promessa de {instância :, módulo:}
  • Mude para um novo módulo (bytes , importações ), módulo de retorno
  • Mudar para WebAssembly.compile (bytes , importações ) retorna promessa de instância

Declare em algum lugar a expectativa de que a instanciação será rápida se as importações da correspondência de compilação forem instanciadas.

WDYT?

Ops, eu pretendia colocar as importações como um argumento em Instance . Não estou convencido de que seja necessário para Module ou compile . [Editar: porque se você os tivesse, simplesmente chamaria instantiate ]

Isso significaria que para o caso assíncrono de ponta a ponta, você pode saber que estará vinculado a uma memória hack de 4 GB, mas não para um kernel de filtro JITed ou um item compilado de fundo (a menos que você também crie um instância de distância)?

+1 em focar a orientação no par assíncrono de compilar e instanciar - torna a mensagem simples e esconde do desenvolvedor as complexidades do problema de decisão.

Sim, acho que estamos todos de acordo em apontar às pessoas:
Primeira vez:
WebAssembly.instantiate (bytes, importações) -> promessa de {módulo, instância} (módulo de cache para indexeddb)
Segundo tempo:
WebAssembly.instantiate (módulo, importações) -> promessa de {módulo, instância}

Alguma objeção a esse ser o padrão principal?

Estou indeciso sobre as importações de módulo de compilação / novo. Parece que essa pode ser uma dica útil.
Porém, eu estaria aberto para mencioná-lo como uma possibilidade e adiar a adição desse argumento (pode ser opcional) ao Pós-MVP.

Pensamentos?

@mtrofin (Bem, tecnicamente, apenas instantiate .)

@lukewagner (acho que é isso que @mtrofin significa)

@lukewagner , @flagxor OK, mas estamos mantendo a API de compilação assíncrona, certo?

Que tal este cenário: você obtém um aplicativo como o PhotoShop com toneladas de plug-ins. Cada plugin é um módulo wasm. Você inicia o aplicativo principal e consegue alocar o tamanho mágico da memória que aciona a memória rápida (parece razoável para este cenário - um aplicativo com fome de memória).

Você deseja compilar vários plug-ins em paralelo, portanto, alguns trabalhadores podem fazer isso. Você não pode passar a essas compilações a memória real que usará (correto?). Portanto, dependendo dos padrões, você obtém uma compilação de memória lenta para os plug-ins, que será seguida por uma grande quantidade de recompilações assíncronas para a memória rápida quando os plug-ins forem conectados ao aplicativo.

Se comprarmos este cenário, então parece que pode haver valor em passar algum descritor de memória (para ser claro, sem memória de apoio real) para a API de compilação.

Sim, deve ser possível (até mesmo encorajado) passar Memory para a compilação.

@mtrofin Certo, compile para usos avançados. Suponho que esse exemplo de plug-in é um caso válido em que você deseja _compilar_, _e_ tem um Memory , mas não deseja instanciar (ainda).

@pizlonator A propósito , eu pretendia perguntar anteriormente, assumindo que o hack "jogar se mais de 1000 mapas de 4 GB por processo" é suficiente para resolver as questões de ASLR / segurança, existe _ainda_ uma necessidade de modo lento / modo rápido devido à plataforma restrições de cota de endereço virtual? Porque se não houvesse, certamente seria bom se isso não fosse uma consideração de desempenho até mesmo para usuários avançados. (As APIs instantiate parecem úteis para adicionar pelos outros motivos que mencionamos, é claro.)

Também há aplicativos que podem se beneficiar de um tamanho de memória que é uma potência de dois mais uma área de derramamento, onde o aplicativo já mascara os ponteiros para remover a marcação, de modo que pode mascarar os bits altos para evitar a verificação de limites. Esses aplicativos precisam raciocinar sobre o tamanho da alocação de memória que podem receber antes da compilação, seja modificando as constantes globais usadas para mascarar ou para assar em constantes adequadas enquanto descompactam para wasm.

Há também a otimização do buffer em zero que alguns tempos de execução podem querer aproveitar, e só haverá um buffer por processo.

Também tornaria a plataforma mais amigável se pudesse raciocinar sobre a memória necessária e a memória disponível antes de compilar o aplicativo. Por exemplo, para permitir que o navegador ou aplicativo informe ao usuário que ele precisa fechar algumas guias para executar o aplicativo ou executá-lo sem degradação da capacidade.

Um navegador da web pode querer ter um modo de aplicativo dedicado que seja uma opção para os usuários, onde eles podem estar executando em um dispositivo limitado e precisam de toda a memória e desempenho que podem obter apenas para executar bem um único aplicativo. Para isso, ele precisa ser capaz de raciocinar sobre os requisitos desde o início.

A memória não deve precisar ser alocada antes da compilação, em vez de uma reserva fundamentada feita. Em um dispositivo limitado, a compilação sozinha pode usar muita memória, portanto, mesmo uma grande alocação de VM pode ser um empecilho.

Não são questões novas, estão em discussão há anos. O gerenciamento de recursos de memória é necessário e deve ser coordenado com a geração de código.

@lukewagner Acho que sim, porque se

  • Eu me preocuparia com um ataque à superfície que precisasse desse teto para cair.
  • Eu me preocuparia com otimizações em outras partes da pilha, reduzindo a quantidade de espaço de endereço virtual que está disponível para nós, o que exigiria que reavaliamos se o teto é baixo o suficiente.
  • Eu me preocuparia em evitar estilos de programação que criam deliberadamente milhares de módulos. Por exemplo, eu sei que a maioria dos clientes da estrutura JavaScriptCore cria uma VM, faz um pequeno trabalho e, em seguida, a destrói. Se WebAssembly for usado da mesma forma em JS e JSC em Objective-C, então para fazê-lo funcionar em sistemas de 64 bits, o GC teria que saber que se você alocar 1000 memórias - mesmo se cada uma fosse pequena - você teria que GC, caso a próxima alocação seja bem-sucedida, pois essas 1000 memórias agora estão inacessíveis. A capacidade de alocar memórias de hack que não sejam de 4 GB depois que já houver, digamos, 10 memórias de hack de 4 GB vivas significaria que o GC não teria que mudar muito sua heurística. Não teria que fazer um GC quando você alocasse o 1001º módulo em seu loop de instanciar-> executar-> morrer. Isso seria um benefício para padrões que usam uma memória minúscula. Qualquer coisa abaixo de 1 MB, e começa a fazer sentido ter 1000 deles. Posso imaginar pessoas fazendo coisas úteis em 64 KB.
  • Eu me preocuparia se isso fosse menos útil para outros contextos JavaScript. Quero deixar a porta aberta para que os clientes JSC C API e Objective-C API tenham acesso à API WebAssembly a partir de seu código JS. Esses clientes provavelmente prefeririam um pequeno limite no número de memórias de 4 GB que alocamos. É tentador até mesmo tornar essa cota configurável em tal contexto.

Gosto que a API aprimorada elimine a necessidade de ter um teto artificial no número de memórias, ou a necessidade de recompilar, ou outras coisas indesejáveis. Não gosto de tetos artificiais, a menos que as tolerâncias sejam muito grandes, e não acho que seja o caso aqui.

@pizlonator Justo o suficiente, e é uma API mais limpa / simples, então acho que é bom adicionar.

Quanto a por que não estou preocupado com os itens que você mencionou (neste momento):

  • O limite pode muito bem precisar ser aumentado; isso é fácil.
  • Em qualquer limite razoável, apenas uma pequena fração do espaço de endereço total de 64 bits será usada, portanto, não estou ciente de qual é esse vetor de ataque aqui; determinado conteúdo tem muitas maneiras de OOM em si
  • Impulsionamos a heurística GC proporcional ao tamanho da reserva e, assim, passar de Memory s apenas leva a GC mais agressivo. Mais GC não é ótimo, mas não tenho certeza se esse será um padrão comum.

Mas quem sabe o que veremos no futuro, então suponho que seja útil ter a flexibilidade incorporada agora.

Acho uma má ideia tentar trazer à tona muitos detalhes de implementação na API js (especialmente detalhes específicos de arquitetura / plataforma). Acho que deve ser suficiente para as implementações terem seus próprios limites internos para memória rápida.

Ter uma função de instância assíncrona parece razoável, mas acho que devemos usar outra coisa se quisermos para dar dicas do compilador. Por exemplo, poderíamos estender os módulos para ter uma seção de sinalizadores que solicita a otimização para singleton, otimização para muitas instâncias, otimização para tempo de carregamento, otimização para desempenho previsível, etc. mas dá aos desenvolvedores um botão para girar e a concorrência manterá os fornecedores de navegadores honestos.

Na sexta-feira, 28 de outubro de 2016 às 2h15, JF Bastien [email protected]
escreveu:

Sim, deve ser possível (até mesmo encorajado) passar memória para o
compilação.

Acho que deveria ser desencorajado em favor de não
importar / exportar memórias em tudo. Afinal, se um módulo não importa
ou memória de exportação, a memória pode ser reservada no momento da compilação. Eu sei nós
querem ser capazes de lidar com eficiência com o sofisticado módulo-fu que alguns
os aplicativos vão querer fazer, mas espero que os aplicativos WASM monolíticos
ser mais comum do que esperamos. Talvez eu esteja em minoria aqui, mas
Eu prefiro ver menos módulos com menos vinculação dinâmica.

-

Você está recebendo isso porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/WebAssembly/design/issues/838#issuecomment -256805006,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/ALnq1KXrUWaegRZwEhznmT1YcyI33IN9ks5q4T6TgaJpZM4Kh1gM
.

Eu concordo totalmente com você, acho que os aplicativos monolíticos serão o principal caso de uso, pelo menos nos próximos dois anos após o mvp. Se preocupar com o que acontece quando você tem 10s de milhares de módulos carregados é supor muito sobre um ecossistema wasm que ainda não existe.

Então, o que resta a fazer para resolver esse problema? Uma coisa seria atualizar http://webassembly.org/getting-started/js-api , o que posso fazer. Outra seria para binaryen emitir isso por padrão (soa bem, @kripken?). @titzer Canary implementa WebAssembly.instantiate ?

Algo mais?

@lukewagner : não tenho certeza do que você está perguntando, a discussão nesta edição é muito longa. Você quer alterar o binaryen da sincronização atual da API WebAssembly.Instance que ele usa para usar uma das novas APIs baseadas em promessa propostas aqui? Isso é necessário - estamos removendo a maneira antiga?

@kripken Certo, mudando para usar WebAssembly.instantiate . Não estamos removendo a forma antiga, mas a nova é mais eficiente e deve ser usada por padrão.

Poderíamos usar a API baseada em promessa ao gerar HTML, mas muitos usuários geram JS e adicionam automaticamente etapas assíncronas não é trivial. Podemos documentar isso para as pessoas. Mas acho que devemos fazer tudo isso apenas quando isso chegar a todos os navegadores.

@kripken Não tenho certeza se entendi: a abordagem atual é não usar nenhuma API assíncrona?

Sim . Veja este problema para adicionar coisas assíncronas.

O @kripken Node teria promessas de trabalho, eu presumo. Mesmo os shells têm uma maneira de drenar explicitamente a fila de promessas (e, assim, executar todas as resoluções de maneira síncrona); no SM é drainJobQueue() e no V8 ouvi dizer que existe um %RunMicroTasks() . Parece que você poderia simplesmente apresentar o teste WebAssembly.instantiate e usá-lo quando estiver presente por padrão.

Claro, mas primeiro, o suporte do Promise pode estar no node.js mais recente, mas não nas versões comumente usadas (versão padrão em distros Linux, por exemplo). E em segundo lugar, o maior problema é que quando emitimos HTML, temos controle sobre como a página é carregada (emcc emite o código de carregamento para o usuário), enquanto quando emitimos JS, presume-se que o JS execute apenas linearmente e os usuários dependem sobre isso, por exemplo, eles podem ter outra tag de script logo após esse JS. Nesse caso, o usuário escreve seu próprio código de carregamento.

Como resultado de ambos, conforme mencionado anteriormente, podemos usar APIs Promise ao emitir HTML (então você definitivamente não está no shell e tem controle sobre o carregamento), mas não ao emitir JS. Lá, podemos apenas documentá-lo.

O Node tem versões que suportam WebAssembly, mas não Promise? O pessoal do Node se preocupa com essas versões?

Eu não entendo a coisa do JS linear. Se você sempre retornar uma promessa, isso não funcionará (os usuários do código precisam consumir a promessa)?

Não sei a resposta para a primeira pergunta, mas um polyfill pode permitir que o wasm seja executado em uma versão de nó que carece de promessas. Embora possa ser possível fazer o polyfill de promessas também, já que o node tem uma versão de setTimeout há um tempo, eu acho, mas não tenho certeza sobre isso também.

Sobre o problema da linha reta: o emcc emite JS que configura o tempo de execução e se conecta ao código compilado. Algum JS em uma tag de script logo após pode chamar o código compilado, por exemplo, usando ccall . Em outras palavras, a saída JS do emcc não é uma promessa, então não tenho certeza do que você entende por "devolver uma promessa" - quem a devolveria e quem a receberia? Mas em qualquer caso, conforme mencionado anteriormente, o caminho recomendado é para emcc emitir HTML, caso em que podemos criar código de carregamento assíncrono. Acontece que alguns usuários preferem controlar o carregamento de forma mais direta. Precisamos incentivá-los a usar o material wasm assíncrono se for melhor.

O Nó IMO com WebAssembly, mas sem Promises, não é uma restrição de design com a qual devemos nos preocupar. Um polyfill para WebAssembly é muito bobo nesse contexto.

Você está descrevendo o que o código faz hoje. Não entendi bem, mas gostaria de fazer backup: quero que o código nas páginas da web use a API Promise. Emscripten é um produtor desse código. Não entendo o que o impede de emitir código que usa promessas. Estou totalmente bem se você disser "isso é um trabalho significativo porque não é assim que funciona hoje, mas vamos chegar lá". Mas, pela nossa discussão, não tenho certeza se é isso que você está dizendo.

O problema que você apontou é apenas sobre o uso de APIs assíncronas para armazenamento em cache? Acho que é um começo, mas o estado final que gostaria de chegar é onde a API assíncrona é usada mesmo no primeiro carregamento.

Por que um polyfill para wasm tolo no nó? Ainda parece útil nesse contexto, mesmo que menos do que em outros casos :)

Novamente: Emscripten vai usar a API promessa quando ele emite HTML. E esse é o caminho recomendado. Portanto, a resposta à sua pergunta é "sim". Não é um trabalho significativo. Está esboçado nessa edição, que sim, foca no armazenamento em cache, mas adicionei notas da (antiga) discussão off-line de que, ao fazer isso, também devemos fazer várias outras otimizações assíncronas, já que podemos e é trivial.

Isso responde às suas preocupações?

Tudo o que estou dizendo é que quando o Emscripten emite JS - o caminho menos recomendado - então as garantias sobre essa saída não são consistentes com o código fazendo alguma mágica assíncrona internamente. Estaríamos quebrando o código das pessoas, o que não queremos fazer. Como eu disse, alguém pode escrever um JS que seja executado de forma síncrona logo após aquele JS que presume que está pronto. Portanto, não poderemos usar promessas nesse caso. Imagina isto:

`` ``

`` ``

doSomething precisa de Module.my_func para existir. Se output.js acabou de retornar uma promessa, então ela ainda não existe. Portanto, esta seria uma mudança significativa.

Isso faz sentido agora?

Por que um polyfill para wasm tolo no nó? Ainda parece útil nesse contexto, mesmo que menos do que em outros casos :)

Um polyfill para wasm não é bobo. Catering para instalações do Node que não têm wasm, polyfill e não prometem, mas não polyfill isso, é bobagem. É meia-boca. Eles deveriam pegar a outra metade da bunda 😁

Novamente: o Emscripten usará a API de promessa ao emitir HTML. E esse é o caminho recomendado. Portanto, a resposta à sua pergunta é "sim". Não é um trabalho significativo. Está esboçado nessa edição, que sim, foca no armazenamento em cache, mas adicionei notas da (antiga) discussão off-line de que, ao fazer isso, também devemos fazer várias outras otimizações assíncronas, já que podemos e é trivial.

Isso responde às suas preocupações?

OK, assim está bom! Contanto que a maioria das páginas da web use a API de promessa, fico feliz.

[recorte]

Isso faz sentido agora?

sim. Obrigado por explicar.

Porém, do meu ponto de vista, o Emscripten não está mais no negócio de emitir JS! Seu exemplo faz sentido para Ye Olden Codes, mas coisas novas devem assumir promessas IMO.

A propósito, eu olhei como alternar webassembly.org/demo para usar instantiate e é um pouco complicado porque o new Instance síncrono atual ocorre em um contexto que deseja um resultado síncrono. Assim, quando tivermos o Binaryen atualizado para emitir instantiate por padrão, seria bom reconstruir a demo do AngryBots do zero.

Sim, mas note que uma reconstrução do zero pode não ser suficiente - eu acredito que o Unity usa seu próprio carregamento e código HTML. Portanto, precisaremos documentar e comunicar esse problema, conforme mencionado anteriormente, para que eles possam fazer as coisas necessárias (ou talvez possamos fazer com que eles deixem o emcc emitir o html, mas não sei se isso é viável para eles).

Sim, mas note que uma reconstrução do zero pode não ser suficiente - eu acredito que o Unity usa seu próprio carregamento e código HTML. Portanto, precisaremos documentar e comunicar esse problema, conforme mencionado anteriormente, para que eles possam fazer as coisas necessárias (ou talvez possamos fazer com que eles deixem o emcc emitir o html, mas não sei se isso é viável para eles).

Dadas as desvantagens potenciais de não usar a API WebAssembly.instantiate , acho que vale a pena pedir a eles que considerem usá-la.

Essas desvantagens estão documentadas? Uma orientação clara sobre isso no site principal do wasm ou na página wiki do

Eu mesmo dei uma olhada neste longo problema e não estou certo das desvantagens ainda, então quero ler isso também :)

@kripken O desafio com o código atual é que binaryen / emscripten fornece apenas as importações necessárias (necessárias como argumentos para instantiate ) ao mesmo tempo que as exportações são necessárias de forma síncrona. Se as importações podem ser disponibilizadas "antecipadamente", então é bastante trivial adicionar um WebAssembly.instantiate ao final do XHR assíncrono (como fiz na demonstração atual com o assíncrono compile ). Portanto, não acho que isso exigirá muito trabalho da parte da Unity. Além disso, pelo que tenho visto , nosso build atual do wasm do AngryBots não é o ideal e precisa ser atualizado de qualquer maneira.

Oh, eu não entendi que ter as importações disponíveis antes mesmo de o wasm XHR começar é a chave aqui. Isso é muito mais complicado então. Portanto, a maior parte do que eu disse antes está errado.

Para termos as importações, precisamos fazer o download, analisar e executar toda a cola JS. Se fizermos tudo isso antes mesmo de o wasm XHR começar, será um esquema de carregamento e um conjunto de compensações muito diferente do que temos agora. Em particular, para projetos pequenos e médios, talvez isso nem fosse um aumento de velocidade, acho que teríamos que medir isso - se ainda não o fizemos?

Isso não seria algo simples para a Unity fazer. Isso exigiria mudanças significativas nas emissões do emcc do código, para que o JS glue pudesse ser executado antes que o código compilado estivesse pronto.

Talvez queiramos considerar um novo modelo de JS emitido, um arquivo JS para as importações, um arquivo JS para o resto? E seria opt-in, então não quebraríamos ninguém. De qualquer forma, há muita consideração e, sem medições, é difícil adivinhar o que é ideal.

@kripken Não antes do XHR, mas depois de concluído, e algum tempo antes de começarmos a executar o script que deseja acesso síncrono ao objeto de exportação. Eu esperava que fosse tão simples quanto colocar esse código de exportação em algum retorno de chamada chamado quando a instanciação for resolvida.

Hmm, acho que ainda não entendi totalmente isso, desculpe (em particular, não entendo por que a assincronia interage com a obtenção das importações - poderia o benefício de ter as importações não funcionarem de forma síncrona?). Mas parece que os problemas que mencionei acima ainda são relevantes, mesmo que seja após a conclusão do XHR - ou seja, temos um único script agora que gera as importações e também recebe as exportações. Dividir isso pode levar a compensações - só precisamos medir quando tivermos tempo.

Sobre colocar o código de uso de exportações em um retorno de chamada, infelizmente isso não funcionará apenas pelos motivos mencionados anteriormente, mas poderíamos investigar como parte dessas medidas adicionando algum novo modo de compilação.

A assinatura de instantiate é WebAssembly.instantiate(bytes, importObj) , então para disparar a compilação assíncrona + instanciar, você precisa passar importObj .

Falando offline, @lukewagner me impressionou que o principal problema aqui é ter a memória ao compilar o módulo. De modo geral, então, parece que há três problemas relacionados à facilidade de uso dessa nova API na prática:

  1. Fornecendo a memória durante a compilação.
  2. Fornecendo todas as importações durante a compilação (superconjunto do anterior).
  3. Fazendo tudo isso de forma assíncrona.

Dado como o conjunto de ferramentas funciona atualmente, fazer 2 + 3 é difícil, como descrito acima, porque vai quebrar os usuários existentes. Podemos fazer isso, mas possivelmente não queremos ativá-lo por padrão, pelo menos não inicialmente - precisaríamos consultar os membros da comunidade etc. E para fazê-lo realmente bem - sem novas despesas - exigirá tempo e trabalho (fazer isso rapidamente pode ser feito adicionando uma camada de indireção nas importações ou nas exportações).

Mas algumas outras opções são trivialmente fáceis de usar:

  • 1 + 3 exigiria uma nova API como instantiate(bytes, Memory) -> Promise . A razão pela qual isso é fácil de usar é que a memória pode ser criada antecipadamente de qualquer maneira (enquanto quase todas as outras importações são funções JS, que não podemos ter no início).
  • 2 por si só, sem 3, ou seja, new Instance(bytes, imports) . Ou seja, compilação síncrona em dados binários + importações. A razão pela qual isso é fácil de usar é que nosso código atual faz isso: instance = new WebAssembly.Instance(new WebAssembly.Module(getBinary()), imports) portanto, apenas dobraríamos isso em uma chamada de API.

Acho que a última opção faz sentido. Basicamente, significa adicionar uma versão de sincronização da nova API, tornando as coisas mais simétricas com nossa opção de compilação síncrona existente. E não vejo razão para amarrar a nova otimização de conhecer a memória em tempo de compilação à compilação assíncrona? (assíncrono é ótimo, mas é um problema separado?)

Sugiro metaobjetos que descrevam as importações (a memória) para quebrar a restrição de que as importações precisam ser alocadas antes da compilação. Então, o método instantiate poderia gerar um erro se a memória fornecida não corresponder ao esperado e não atrasar a recompilação.

Também seria muito útil ser capaz de compilar antes de alocar a memória linear em um dispositivo de memória restrita, também ser capaz de otimizar a compilação para memória alocada em zero no espaço de endereço linear, e talvez para memória com zonas de guarda, e para otimize para memória com um tamanho máximo fixo que pode ser uma potência de dois mais uma zona de vazamento. Este metaobjeto também pode ser usado por um decodificador / reescritor do usuário wasm para emitir código otimizado para as características de memória negociadas.

Isso é algo que está sendo sugerido como necessário por volta do início deste projeto, há muitos anos!

@kripken Embora as APIs de sincronização sejam, acredito, tecnicamente necessárias, elas definitivamente devem ser o caminho não recomendado, ou então apresentaremos toneladas de jank de thread principal desnecessário, uma regressão em comparação com asm.js +

Tenho pretendido fornecer um exemplo melhor de por que precisamos desencorajar explicitamente (ou, como argumentei no passado, até mesmo proibir) a compilação / instanciação síncrona. Esperançosamente, isso fornecerá material adicional para discussão.

Vamos diminuir o zoom e falar sobre a experiência do usuário por um segundo.


Aqui está uma comparação entre carregar a demonstração do

comparison

A compilação síncrona fornece uma experiência terrível para o usuário e quebra as barras de progresso ou qualquer outra indicação visual de carregamento do aplicativo.

Seja qual for o esforço, acho que precisamos trabalhar juntos para garantir que Emscripten, Unity e outras ferramentas exportem WebAssembly de carregamento assíncrono por padrão. cc @jechter @juj Além disso, acho que precisamos tornar proibitivamente difícil para os desenvolvedores cair na armadilha de escrever código de carregamento síncrono.

Precisamos encorajar explicitamente WebAssembly.compile & WebAssembly.instantiate e desencorajar new WebAssembly.Module & new WebAssembly.Instance .


Vamos mergulhar um pouco mais fundo e ver o quão ruim é a barra de progresso à direita, para WebAssembly.

Aqui está um rastreamento capturado com o painel de desempenho DevTools:

screen shot 2016-12-28 at 1 26 59 pm

Demora 30 segundos no meu MacBook Air para mostrar qualquer barra de progresso.

O que leva esse tempo? Vamos aproximar cerca de 20 segundos após o download do módulo wasm, quando o thread principal está totalmente bloqueado:

screen shot 2016-12-28 at 2 18 36 pm

A compilação está levando ~ 20s e a instanciação está levando ~ 2s.

Observe que, neste caso, o carregador do Unity já está chamando o WebAssembly.compile assíncrono se for suportado, então os anos 20 são porque o V8 ainda faz uma compilação síncrona nos bastidores. cc @titzer @flagxor isso é algo que o V8 realmente precisa consertar.

O erro de instanciação 2s é devido ao código do carregador do Unity chamando o new WebAssembly.Instance síncrono. Isso é o que realmente precisamos corrigir no código do Unity e no Emscripten.


Acho que também vale a pena mencionar que esse não é _ simplesmente_ o risco de um desenvolvedor dar um tiro no próprio pé ou não. A página da web média inclui dezenas de scripts de terceiros: todos esses scripts têm o potencial de incluir WebAssembly de manutenção de documentos importantes.

(Se quiser explorar o rastreamento em mais detalhes, você pode ver a linha do tempo completa aqui: https://chromedevtools.github.io/timeline-viewer/?loadTimelineFromURL=https://www.dropbox.com/s/ noqjwql0pq6c1wy / wasm? dl = 0)

Concordo 100% que assíncrono é ótimo :) Mas estou dizendo que é ortogonal à otimização de obter a memória ao mesmo tempo que compilar o módulo. E que não podemos trivialmente obter assíncronos + essa otimização - podemos obter essa combinação, mas ao custo de alguma sobrecarga ou de tempo se introduzirmos um novo sinalizador para ele e encontrarmos uma maneira de torná-lo padrão .

Portanto, se quisermos que a otimização de memória / compilação agora, por padrão, e com eficiência total, vinculá-la a ser também assíncrona é um problema. Mas se não for urgente, ou se não estivermos ativados por padrão, ou se tivermos alguma nova sobrecarga, não há problema.

Como os desenvolvedores estão optando pelo wasm, acho que faz sentido aproveitar a oportunidade para alterar um pouco a estrutura de nível superior com um novo padrão (com uma forma de optar pelo comportamento antigo, se necessário). Pessoas usando sincronização

@kripken Acho que você chegou a conclusões diferentes das que eu, @lukewagner e @ s3ththompson , porque para você a parte mais importante é oferecer uma transição perfeita do asm.js para os desenvolvedores existentes.

Isso está correto?

Eu concordo que essa é uma preocupação válida. Eu pesei menos porque IMO com WebAssembly estamos trazendo muito mais desenvolvedores do que o asm.js. Eu gostaria de evitar a queima de primeiros usuários, mas os desenvolvedores existentes do IIUC são bastante flexíveis e querem uma compilação assíncrona.

Se presumirmos que eles desejam uma compilação assíncrona e estão dispostos a refatorá-la para obtê-la, então tudo o que resta é ter memória no momento da compilação, que é o que esta API fornece. É altamente desejável porque evita uma armadilha.

Anteriormente, você expressou uma preocupação com a quantidade de trabalho envolvido no lado do Emscripten para apoiar isso. Você ainda acha que isso é um problema?

FWIW, Unity 5.6 usará WebAssembly.instantiate.

Jonas

Em 28 de dezembro de 2016, às 23h42, Seth Thompson [email protected] escreveu:

Tenho pretendido fornecer um exemplo melhor de por que precisamos desencorajar explicitamente (ou, como argumentei no passado, até mesmo proibir) a compilação / instanciação síncrona. Esperançosamente, isso fornecerá material adicional para discussão.

Vamos diminuir o zoom e falar sobre a experiência do usuário por um segundo.

Aqui está uma comparação entre carregar a demonstração do AngryBots com chamadas assíncronas (via asm.js), à esquerda e chamadas síncronas (implementação atual do WebAssembly), à direita.

A compilação síncrona fornece uma experiência terrível para o usuário e quebra as barras de progresso ou qualquer outra indicação visual de carregamento do aplicativo.

Seja qual for o esforço, acho que precisamos trabalhar juntos para garantir que Emscripten, Unity e outras ferramentas exportem WebAssembly de carregamento assíncrono por padrão. cc @jechter @juj Além disso, acho que precisamos tornar proibitivamente difícil para os desenvolvedores cair na armadilha de escrever código de carregamento síncrono.

Precisamos encorajar explicitamente WebAssembly.compile & WebAssembly.instantiate e desencorajar novo WebAssembly.Module e novo WebAssembly.Instance.

Vamos mergulhar um pouco mais fundo e ver o quão ruim é a barra de progresso à direita, para WebAssembly.

Aqui está um rastreamento capturado com o painel de desempenho DevTools:

Demora 30 segundos no meu MacBook Air para mostrar qualquer barra de progresso.

O que leva esse tempo? Vamos aproximar cerca de 20 segundos após o download do módulo wasm, quando o thread principal está totalmente bloqueado:

A compilação está levando ~ 20s e a instanciação está levando ~ 2s.

Observe que, neste caso, o carregador do Unity já está chamando o WebAssembly.compile assíncrono se for compatível, então os anos 20 são porque o V8 ainda faz uma compilação síncrona nos bastidores. cc @titzer @flagxor isso é algo que o V8 realmente precisa consertar.

O erro de instanciação 2s é devido ao código do carregador do Unity chamando o novo WebAssembly.Instance síncrono. Isso é o que realmente precisamos corrigir no código do Unity e no Emscripten.

Acho que também vale a pena mencionar que não se trata apenas do risco de um desenvolvedor dar um tiro no próprio pé ou não. A página da web média inclui dezenas de scripts de terceiros: todos esses scripts têm o potencial de incluir WebAssembly de manutenção de documentos importantes.

(Se quiser explorar o rastreamento em mais detalhes, você pode ver a linha do tempo completa aqui: https://chromedevtools.github.io/timeline-viewer/?loadTimelineFromURL=https://www.dropbox.com/s/ noqjwql0pq6c1wy / wasm? dl = 0)

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

@jfbastien Talvez eu não entenda o que você quer dizer com "conclusões". Estou apenas apresentando opções aqui. Eu mesmo não tenho uma conclusão.

Se quisermos a opção Memory-at-Compile-time agora, propus algumas opções que podem nos permitir o mais rápido possível, modificando trivialmente nossa compilação de sincronização atual.

Ou se quisermos essa opção agora de forma assíncrona, proponho uma opção ("1 + 3", ou seja, não receber todas as importações em tempo de compilação, apenas a Memória) que pode nos deixar também o mais rápido possível.

Ou se quisermos nos concentrar agora em uma versão assíncrona dessa opção com a API atual (não "1 + 3"), então podemos conseguir isso também, será necessário um planejamento cuidadoso porque seria uma alteração significativa. Não acho que seja uma opção para nós aqui decidirmos quebrar os usuários existentes, então precisaríamos consultar a comunidade. Possivelmente, haverá poucas preocupações e podemos simplesmente fazer isso; nesse caso, quanto esforço dependerá de quanta sobrecarga estamos dispostos a tolerar - se não podemos aceitar nenhuma sobrecarga, então pode ser muito. Ou possivelmente haverá preocupações, o que é minha intuição pessoal - qualquer alteração significativa deve ser feita gradualmente - nesse caso, levará mais tempo, provavelmente começaríamos com uma nova opção e, eventualmente, planejaríamos torná-la padrão.

Novamente, nenhuma conclusão minha. Todos os itens acima são opções. Realmente depende do que você mais se preocupa: memória em tempo de compilação, ou assíncrona, ou ambos; e a questão de tolerar novas despesas gerais versus não; e se você deseja isso com urgência ou pode esperar, etc. etc. Fico feliz em ajudar no lado do emscripten com qualquer uma das opções acima, seja qual for a decisão das pessoas.

Ou se quisermos nos concentrar agora em uma versão assíncrona dessa opção com a API atual (não "1 + 3"), então podemos conseguir isso também, será necessário um planejamento cuidadoso porque seria uma alteração significativa. Não acho que seja uma opção para nós aqui decidirmos quebrar os usuários existentes, então precisaríamos consultar a comunidade. Possivelmente, haverá poucas preocupações e podemos simplesmente fazer isso; nesse caso, quanto esforço dependerá de quanta sobrecarga estamos dispostos a tolerar - se não podemos aceitar nenhuma sobrecarga, então pode ser muito. Ou possivelmente haverá preocupações, o que é minha intuição pessoal - qualquer alteração significativa deve ser feita gradualmente - nesse caso, levará mais tempo, provavelmente começaríamos com uma nova opção e, eventualmente, planejaríamos torná-la padrão.

Para ter certeza de que entendi:

  1. Quais são as despesas gerais?

    • Essas sobrecargas são inerentes ao Async + Memória em tempo de compilação ou são restrições de implementação que podem ser corrigidas posteriormente? IMO, se eles forem inerentes a A + M, então devemos consertar a API o mais rápido possível.

    • As despesas gerais do IIUC são temporárias nas importações ou nas exportações e não são inerentes à A + M, correto? Você poderia detalhar isso um pouco mais?

  2. De quais usuários estamos falando? Os usuários do asm.js que desejam o mesmo código também para o wasm?

Dito de outra forma: qual seria o estado final ideal para WebAssembly se tivéssemos tempo infinito e nenhum "legado"? Acho que projetamos para esse estado final ideal e você está apontando obstáculos no caminho, mas ainda não tenho certeza se é esse o caso. Nesse caso, não tenho certeza se tenho informações suficientes para descobrir se o WebAssembly precisa de uma solução temporária para facilitar a transição ou se a carga temporária é aceitável para um subconjunto de usuários do Emscripten (e quais usuários).

Sobre as sobrecargas em algumas das opções: Conforme descrito acima, o complicado é que devemos enviar as importações e receber as exportações de forma síncrona no momento, e que não temos as importações JS até que o JS esteja pronto (mas temos a memória !). Uma maneira de contornar isso é adicionar uma camada de indireção nas importações ou nas exportações. Por exemplo, poderíamos fornecer thunks em vez das importações verdadeiras muito cedo (no HTML, antes do JS), então parecemos fornecê-los de forma síncrona (mas são apenas os thunks, que são triviais de criar mesmo antes de termos o JS) . Então, mais tarde, quando tivermos o JS, podemos atualizar os thunks para apontar para o lugar certo. Isso adicionaria sobrecarga de outra chamada JS e uma pesquisa de objeto JS para cada chamada de importação. Além disso, alguma sobrecarga no tamanho do código e na inicialização.

Sobre quebrar usuários: Queremos que os usuários emscripten existentes sejam capazes de virar uma bandeira e começar a trabalhar com o wasm . Muitos usuários e parceiros estão felizes com isso. Ele permite que eles comparem asm.js e wasm e permite que os projetos nos quais eles gastaram esforços para portar se beneficiem do wasm sem nenhum esforço de portabilidade. E evita o risco de que, se eles também precisarem assincronizar seu código de inicialização durante a portabilidade para o wasm, e algo quebrar, eles possam culpar o wasm desnecessariamente.

que não temos as importações JS até que o JS esteja pronto (mas temos a memória!)

Isso parece acidental e não algo que devemos transformar no tempo para sempre com uma API. Além disso, pelas razões explicadas por @ s3thompson , mesmo ignorando o problema da memória no tempo de compilação, precisamos que a instanciação seja assíncrona de qualquer maneira . Portanto, concordo com @jfbastien que temos nossa API de estado final ideal aqui e não deve ser isso que comprometemos aqui.

Sobre interromper usuários: se oferecemos um caminho de migração simples (como um evento / promessa "onload"), deve ser simples para os usuários migrar, com a cenoura sendo todas as vitórias de desempenho principais. Não acho que tenhamos qualquer evidência de que o backcompat de origem do asm.js exato seja um requisito difícil para a configuração padrão; temos muitos exemplos do contrário: usuários dispostos a fazer tudo para alcançar o desempenho ideal, já que esse é, obviamente, o ponto principal.

Falando com @lukewagner offline, achamos que a melhor coisa é começar habilitando a instanciação para wasm na configuração padrão recomendada em emscripten, adicionando uma camada de indireção (como descrito acima, mas nas exportações). Provavelmente, ele terá uma sobrecarga desprezível para a maioria dos casos de uso. Isso nos dá os benefícios que todos queremos aqui.

Eventualmente, podemos nos livrar dessa pequena sobrecarga, mas será uma mudança significativa que exigirá muito mais planejamento e discussão na lista de e-mails, etc., já que não há uma maneira obviamente boa / certa de fazer isso, percebemos. Mas, como achamos que a sobrecarga da camada de indireção é muito baixa, essa parte não é urgente.

@kripken ótimo! Seria útil ter um diagrama mostrando diferentes instâncias / JS / importações / exportações / memória / tabelas. Você deseja mover esta discussão para outro lugar (Emscripten / binaryen repo)? Eu tenho uma imagem mental de como o código C ++ deveria ser organizado, mas agora é bastante óbvio que você não tem a mesma imagem! Você tem mais experiência lá, então eu adoraria aprender com isso e ajudar no que puder.

@jfbastien : claro. Ainda não tenho certeza do que você está procurando em um diagrama, mas sim, talvez outro lugar seja melhor. Para a implementação disso no emscripten existe o problema mencionado antes, https://github.com/kripken/emscripten/issues/4711 , sinta-se à vontade para abrir outro se não cobrir o que você deseja.

O Emscripten IIUC usa isso por padrão agora. Fechando.

Para acompanhar o comentário de @s3ththompson , observe que a compilação e instanciação síncronas são úteis. Notavelmente, recentemente me deparei com uma situação em que queria carregar e compilar de forma síncrona um módulo WebAssembly em Node.js ( v7.7.2 ). Se apenas APIs que retornam promessas estiverem disponíveis, isso significa que não poderei fornecer uma exportação síncrona.

Ao decidir se fornecerá assíncrono, sincronização ou ambas as APIs, lembre-se de que um contexto de navegador não é o único ambiente no qual as pessoas desejam usar o WebAssembly. Várias pessoas, inclusive eu, estão interessadas no potencial do WebAssembly como um destino de compilação eficiente combinado com o tempo de execução Node.js, semelhante ao JVM. Embora a importação / exportação assíncrona possa chegar ao Node.js, a importação e exportação síncronas continuarão sendo o padrão dominante em um futuro previsível. Nesse caso, a capacidade de carregar um módulo WebAssembly e compilar e instanciar de forma síncrona esse módulo é altamente útil.

@kgryte Eu deveria ter esclarecido que meu comentário se referia principalmente ao navegador como um contexto de execução. Nós pousamos em uma superfície de API que ainda expõe as APIs síncronas. Os navegadores podem impor um limite de tamanho aos módulos passados ​​para as APIs síncronas (o Chrome já faz, por exemplo), mas esse limite é configurável pelo incorporador e não deve ser aplicado ao Node.

@ s3ththompson Obrigado pelo esclarecimento.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

sebmarkbage picture sebmarkbage  ·  41Comentários

jfbastien picture jfbastien  ·  244Comentários

bguiz picture bguiz  ·  93Comentários

jfbastien picture jfbastien  ·  44Comentários

kripken picture kripken  ·  37Comentários