1. ¿Qué?
WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.
Un formato portátil, pequeño y de carga rápida (binario), adecuado para compilar en la Web
El objetivo principal es admitir aplicaciones de alto rendimiento en el entorno web. Pero el diseño no se basa en características web, ni proporciona funciones para características web, y también se puede usar en otros entornos.
Una comprensión simple es definir un formato de destino de compilación que pueda lograr un rendimiento de ejecución casi nativo en cualquier entorno que admita este formato. Equivale a permitir la expansión de módulos nativos. En escenarios de rendimiento crítico, use otros lenguajes más adecuados (como C ++) para implementar y luego compile en WebAssembly por adelantado, puede obtener una experiencia de rendimiento comparable a la nativa
Los objetivos del diseño se dividen en 2 aspectos:
Semántica rápida, segura y portátil
Rápido: ejecute con un rendimiento cercano al código nativo y utilice funciones comunes a todo el hardware moderno
Seguridad: el código se verifica y ejecuta en un entorno de zona de pruebas seguro para la memoria para evitar la corrupción de datos o violaciones de seguridad
Bien definido: definición completa y precisa de los procedimientos legales y su comportamiento, de manera que sea fácil inferir informal y formal
Independiente del hardware: compile en todas las arquitecturas modernas, dispositivos de escritorio o móviles y sistemas integrados
Independiente del lenguaje: sin sesgo hacia ningún lenguaje específico, modelo de programación o modelo de objeto
Independiente de la plataforma: puede integrarse en el navegador, ejecutarse como una máquina virtual independiente o integrarse en otros entornos
Abierto: los programas pueden interactuar con su entorno de forma sencilla y universal
Representación eficiente y portátil
Pequeño y exquisito: el formato binario es más pequeño que el texto típico o el formato de código nativo y se puede transmitir rápidamente
Modularidad: el programa se puede dividir en partes más pequeñas, que se pueden transmitir, almacenar en caché y utilizar por separado
Eficiente: se puede decodificar, verificar y compilar rápidamente en una sola pasada (recorrido), lo que equivale a la compilación en tiempo real (JIT) o antes de tiempo (AOT)
Streaming: permite comenzar a decodificar, verificar y compilar lo antes posible antes de que todos los datos estén disponibles
Paralelo: permite que la decodificación, verificación y compilación se dividan en múltiples tareas paralelas independientes
Portabilidad: sin suposiciones sobre arquitecturas que no son ampliamente compatibles con hardware moderno
Los principales navegadores (Chrome, Edge, Firefox y WebKit) promueven conjuntamente el proceso de estandarización:
WebAssembly is currently being designed as an open standard by a W3C Community Group that includes representatives from all major browsers.
PD: Esto está liderado por los fabricantes de navegadores (los cuatro se unen para hacer cosas, vale la pena esperar), por cierto, estándares abiertos (no solo para el entorno web), la motivación proviene del deseo de mejorar aún más el rendimiento del tiempo de ejecución de JS, en V8 Después de la introducción de JIT, es imposible mejorar aún más el rendimiento debido a las limitaciones de las características del lenguaje JS (como los tipos interpretados y débiles). Las capacidades web son cada vez más fuertes, el cliente JS es cada vez más pesado y la necesidad de mejorar aún más el rendimiento de ejecución de JS sigue ahí, por lo que existe el resultado final de WebAssembly
2. Wasm y wast
Sabemos que WebAssembly define un formato binario, este formato es wasm, por ejemplo:
0061 736d 0100 0000 0187 8080 8000 0160
027f 7f01 7f03 8280 8080 0001 0004 8480
8080 0001 7000 0005 8380 8080 0001 0001
0681 8080 8000 0007 9080 8080 0002 066d
656d 6f72 7902 0003 6763 6400 000a ab80
8080 0001 a580 8080 0001 017f 0240 2000
450d 0003 4020 0120 0022 026f 2100 2002
2101 2000 0d00 0b20 020f 0b20 010b
El código C correspondiente a esta cadena de números hexadecimales es:
// 辗转相除法求最大公约数
int gcd(int m, int n) {
if (m == 0) return n;
return gcd(n % m, m);
}
La legibilidad de wasm es igual a 0. Para paliar este problema, se define un formato de texto con mejor legibilidad, llamado wast:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "gcd" (func $gcd))
(func $gcd (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(loop $label$1
(set_local $0
(i32.rem_s
(get_local $1)
(tee_local $2
(get_local $0)
)
)
)
(set_local $1
(get_local $2)
)
(br_if $label$1
(get_local $0)
)
)
(return
(get_local $2)
)
)
(get_local $1)
)
)
Los paréntesis son un poco al estilo Lisp, pero al menos son legibles, por ejemplo:
// 导出了两个东西,分别叫`memory`和`gcd`
(export "memory" (memory $0))
(export "gcd" (func $gcd))
// 函数签名,接受2个int32类型参数,返回int32类型值
(func $gcd (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
// 函数体...就不猜了
PSwast y wasm se pueden convertir entre sí, consulte WABT: The WebAssembly Binary Toolkit para obtener más detalles
Además, puede ver otro comando de texto en el panel Fuente del navegador:
func (param i32 i32) (result i32)
(local i32)
block
get_local 0
i32.eqz
br_if 0
loop
get_local 1
get_local 0
tee_local 2
i32.rem_s
set_local 0
get_local 2
set_local 1
get_local 0
br_if 0
end
get_local 2
return
end
get_local 1
end
Se parece mucho a wast, no sé si hay un nombre, o también pertenece a wast? Esto es convertido por el navegador basado en wasm
3.
Requisitos ambientales del entorno de juego de prueba :
Entorno de compilación C / C ++ Emscripten
Un navegador que admita WebAssembly (el último Chrome lo admite de forma predeterminada)
¿ Le duele el entorno online ? Entorno de demostración: WebAssembly Explorer
COMPILAR y DESCARGAR puede obtener wasm, es fácil de usar
Tenga en cuenta que el entorno predeterminado es C ++. Si desea utilizar C, seleccione C99 o C89 a la izquierda, de lo contrario, el nombre de la función se corromperá, como la pérdida de C ++ 11:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_Z3gcdii" (func $_Z3gcdii))
(func $_Z3gcdii (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(loop $label$1
(set_local $0
(i32.rem_s
(get_local $1)
(tee_local $2
(get_local $0)
)
)
)
(set_local $1
(get_local $2)
)
(br_if $label$1
(get_local $0)
)
)
(return
(get_local $2)
)
)
(get_local $1)
)
)
El nombre de la función se ha compilado en _Z3gcdii. Se supone que algo como el espacio de nombres está causando problemas. No estoy familiarizado con C ++, por lo que uso obedientemente C
PD Además de C / C ++, otros lenguajes también pueden reproducir WebAssembly, como Rust
Descargar el SDK de la plataforma para el entorno local
Siga los pasos de instalación
Si nada más, puede instalarlo aquí, puede probar emcc -v:
INFO:root:(Emscripten: Running sanity checks)
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.22
clang version 4.0.0 (emscripten 1.37.22 : 1.37.22)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: D:\emsdk-portable-64bit\clang\e1.37.22_64bit
INFO:root:(Emscripten: Running sanity checks)
En el entorno Windows, puede encontrar un error de DLL que falta (MSVCP140.dll). Puede instalar manualmente el entorno C ++ requerido. Para obtener más información, consulte MSVCP140.dll no encontrado · Número 5605 · kripken / emscripten
Luego puede intentarlo (guarde el código C anterior como un archivo gcd.c):
emcc ./c/gcd.c -Os -s WASM=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o ./output/gcd.wasm
PD Para obtener más información, consulte el tutorial de Emscripten
El contenido de gcd.wasm es el siguiente:
0061 736d 0100 0000 000c 0664 796c 696e
6b80 80c0 0200 010a 0260 027f 7f01 7f60
0000 0241 0403 656e 760a 6d65 6d6f 7279
4261 7365 037f 0003 656e 7606 6d65 6d6f
7279 0200 8002 0365 6e76 0574 6162 6c65
0170 0000 0365 6e76 0974 6162 6c65 4261
7365 037f 0003 0403 0001 0106 0b02 7f01
4100 0b7f 0141 000b 072b 0312 5f5f 706f
7374 5f69 6e73 7461 6e74 6961 7465 0002
0b72 756e 506f 7374 5365 7473 0001 045f
6763 6400 0009 0100 0a40 0327 0101 7f20
0004 4003 4020 0120 006f 2202 0440 2000
2101 2002 2100 0c01 0b0b 0520 0121 000b
2000 0b03 0001 0b12 0023 0024 0223 0241
8080 c002 6a24 0310 010b
Tenga en cuenta que el nombre del método tendrá como prefijo un guión bajo (_) de forma predeterminada. En este ejemplo, el nombre del método exportado es _gcd. Para obtener más información, consulte Interactuar con el código:
The keys passed into mergeInto generate functions that are prefixed by _. In other words myfunc: function() {}, becomes function _myfunc() {}, as all C methods in emscripten have a _ prefix. Keys starting with $ have the $ stripped and no underscore added.
La interfaz del módulo utilizada en JS debe estar subrayada (no sé si hay un elemento de configuración que pueda eliminarlo)
Cuatro. Pruébalo
WebAssembly.compile(new Uint8Array(`
0061 736d 0100 0000 0187 8080 8000 0160
027f 7f01 7f03 8280 8080 0001 0004 8480
8080 0001 7000 0005 8380 8080 0001 0001
0681 8080 8000 0007 9080 8080 0002 066d
656d 6f72 7902 0003 6763 6400 000a ab80
8080 0001 a580 8080 0001 017f 0240 2000
450d 0003 4020 0120 0022 026f 2100 2002
2101 2000 0d00 0b20 020f 0b20 010b
`.match(/\S{2}/g).map(s => parseInt(s, 16))
)).then(module => {
const instance = new WebAssembly.Instance(module);
console.log(instance.exports);
const { gcd } = instance.exports;
console.log('gcd(328, 648)', gcd(328, 648));
});
La cadena hexadecimal proviene de la demostración en línea, que es consistente con el ejemplo original de wasm. Simplemente coloque estas cosas en la consola de Chrome para su ejecución. Si todo es normal, obtendrá un error:
VM40:1 Uncaught (in promise) CompileError: WasmCompile: Wasm code generation disallowed in this context
Esto se debe a que las restricciones predeterminadas de CSP (Política de seguridad de contenido) son fáciles de resolver, solo active el modo de incógnito (Ctrl / CMD + Shift + N)
Obtendrá la salida:
{memory: Memory, gcd: ƒ}
gcd(328, 648) 8
La primera línea es el contenido de exportación del módulo obtenido al cargar nuestro WebAssembly, incluido un objeto de memoria y un método gcd, y la salida de la segunda línea es el máximo común divisor calculado llamando al módulo de alto rendimiento
WebAssembly.compile y otras API relacionadas pueden hacer referencia a:
JavaScript API-WebAssembly: Definición de especificación
WebAssembly-JavaScript | MDN: contiene ejemplos
Además, la versión compilada localmente requiere importaciones env (y el nombre de la función tiene el prefijo _):
WebAssembly.compile(new Uint8Array(`
0061 736d 0100 0000 000c 0664 796c 696e
6b80 80c0 0200 010a 0260 027f 7f01 7f60
0000 0241 0403 656e 760a 6d65 6d6f 7279
4261 7365 037f 0003 656e 7606 6d65 6d6f
7279 0200 8002 0365 6e76 0574 6162 6c65
0170 0000 0365 6e76 0974 6162 6c65 4261
7365 037f 0003 0403 0001 0106 0b02 7f01
4100 0b7f 0141 000b 072b 0312 5f5f 706f
7374 5f69 6e73 7461 6e74 6961 7465 0002
0b72 756e 506f 7374 5365 7473 0001 045f
6763 6400 0009 0100 0a40 0327 0101 7f20
0004 4003 4020 0120 006f 2202 0440 2000
2101 2002 2100 0c01 0b0b 0520 0121 000b
2000 0b03 0001 0b12 0023 0024 0223 0241
8080 c002 6a24 0310 010b
`.match(/\S{2}/g).map(s => parseInt(s, 16))
)).then(module => {
let imports = {
env: {
memoryBase: 0,
memory: new WebAssembly.Memory({ initial: 256 }),
tableBase: 0,
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};
const instance = new WebAssembly.Instance(module, imports);
console.log(instance.exports);
// 注意下划线前缀
const { _gcd } = instance.exports;
console.log('gcd(328, 648)', _gcd(328, 648));
});
Puede obtener un resultado similar:
{__post_instantiate: ƒ, runPostSets: ƒ, _gcd: ƒ}
gcd(328, 648) 8
Debería ser que Emscripten haya agregado algunas cosas irrelevantes por defecto, que son funcionalmente equivalentes a nuestra versión simplificada
V.
Ventajas, desventajas y escenarios de aplicación El
tamaño del código es pequeño
Aproximadamente 300k (comprimido) la lógica de JavaScript se reescribe con WebAssembly, el volumen es de solo 90k
Pero el uso de WebAssembly requiere la introducción de una biblioteca de clases JavaScript de 50k-100k como infraestructura
La seguridad se mejora ligeramente
Aunque las instrucciones de texto de WebAssembly correspondientes al código fuente aún no están ocultas, el costo inverso es mayor
Mejora del rendimiento
En teoría, WebAssembly tiene un rendimiento de ejecución cercano al nativo, porque omite el proceso de interpretación y el tamaño del archivo también tiene ventajas en términos de transmisión.
Por supuesto, la premisa es que en escenarios donde la cantidad de código comercial es grande y se requiere un rendimiento extremo, en escenarios de ejecución repetida como los puntos de referencia, JIT no es mucho más lento que AOT.
Desventajas
capacidad actualmente limitada:
Solo se admiten algunos tipos de datos básicos (i32 / i64 / f32 / f64 / i8 / i16)
Sin acceso directo a DOM y otras API web
Incapaz de controlar GC
Escenario de aplicación
WebAssembly define un formato binario ejecutable estándar para navegadores, de modo que más desarrolladores puedan participar a través de un mecanismo de compilación unificado para construir un ecosistema Web próspero La visión es buena, pero existen algunos problemas prácticos.
En primer lugar, la intención original de WebAssembly es "admitir aplicaciones de alto rendimiento en el entorno web". Para superar el cuello de botella de rendimiento, los posibles escenarios de aplicación son:
Decodificación de video
Procesamiento de imágenes
Visualización 3D / WebVR / AR
Motor de renderizado
Motor de física
Algoritmo de compresión / encriptación
… Etc. Escenas con mucho cálculo
Por supuesto, es posible que en el futuro también se integre algo de soporte en el navegador, en lugar de usar "extensiones" o similares. Pero el significado real de WebAssembly es proporcionar la capacidad de permitir la autoextensión de módulos "nativos" de alto rendimiento. Después de todo, puede llevar mucho tiempo esperar a que el navegador lo proporcione y esperar hasta que la compatibilidad sea aceptable. Con esta capacidad, No es necesario esperar a que los navegadores principales del mercado admitan una determinada función nativa, puede hacerlo usted mismo y no hay diferencia de compatibilidad. Por el contrario, puede surgir un lote de módulos comunitarios populares y gradualmente ser adoptados como soporte nativo del navegador, devolviendo ecológicamente al entorno web.
WebAssembly de referencia
WebAssembly en la práctica: cómo escribir código: una gran guía
¿Cómo comentar sobre la última tecnología de código de bytes de WebAssembly en los navegadores?
WebAssembly: ¿La bala de plata para resolver los problemas crónicos de JavaScript?
WebAssembly, la nueva era de la Web
¿Se puede polrelleno WebAssembly?
wasm-arrays: biblioteca de empaquetado de matrices WebAssembly