WebAssembly (wasm) es un formato portátil de instrucciones binarias. El mismo código de código wasm se puede ejecutar en cualquier entorno. Para respaldar esta declaración, cada idioma, plataforma y sistema debe poder ejecutar dicho código, por lo que es lo más rápido y seguro posible.
Wasmer es un tiempo de ejecución wasm escrito en
Rust . Obviamente, el lavador se puede usar en cualquier aplicación de Rust. El autor del material, una traducción que publicamos hoy, dice que él y otros participantes en el proyecto Wasmer implementaron con éxito este tiempo de ejecución de código wasm en otros idiomas:
Aquí hablaremos sobre un nuevo proyecto:
go-ext-wasm , que es una biblioteca para Go, diseñada para ejecutar código binario wasm. Al final resultó que, el proyecto go-ext-wasm es mucho más rápido que otras soluciones similares. Pero no nos adelantemos a nosotros mismos. Comencemos con una historia sobre cómo trabajar con él.
Llamar a funciones wasm desde Go
Para comenzar, instale wasmer en un entorno Go (con soporte cgo).
export CGO_ENABLED=1; export CC=gcc; go install github.com/wasmerio/go-ext-wasm/wasmer
El proyecto
go-ext-wasm es una biblioteca Go normal. Cuando se trabaja con esta biblioteca,
import "github.com/wasmerio/go-ext-wasm/wasmer"
la construcción de
import "github.com/wasmerio/go-ext-wasm/wasmer"
.
Ahora vamos a practicar. Vamos a escribir un programa simple que compila en wasm. Usaremos para esto, por ejemplo, Rust:
#[no_mangle] pub extern fn sum(x: i32, y: i32) -> i32 { x + y }
Llamamos al archivo con el programa
simple.rs
, como resultado de compilar este programa obtenemos el archivo
simple.wasm .
El siguiente programa, escrito en Go, realiza la función de
sum
del archivo wasm, pasándole los números 5 y 37 como argumentos:
package main import ( "fmt" wasm "github.com/wasmerio/go-ext-wasm/wasmer" ) func main() {
Aquí, un programa escrito en Go llama a una función de un archivo wasm que se obtuvo compilando código escrito en Rust.
Entonces, el experimento fue un éxito, ejecutamos con éxito el código de WebAssembly en Go. Cabe señalar que la conversión del tipo de datos es automática. Los valores de Go que se pasan al código wasm se convierten en tipos de WebAssembly. Lo que devuelve la función wasm se convierte en tipos Go. Como resultado, trabajar con funciones de archivos wasm en Go se ve igual que trabajar con funciones Go normales.
Funciones de llamada desde el código de WebAssembly
Como vimos en el ejemplo anterior, los módulos de WebAssembly pueden exportar funciones a las que se puede llamar desde afuera. Este es el mecanismo que permite que el código wasm se ejecute en varios entornos.
Al mismo tiempo, los módulos WebAssembly pueden funcionar con funciones importadas. Considere el siguiente programa escrito en Rust.
extern { fn sum(x: i32, y: i32) -> i32; } #[no_mangle] pub extern fn add1(x: i32, y: i32) -> i32 { unsafe { sum(x, y) } + 1 }
Asigne un nombre al archivo con
import.rs
. Compilarlo en WebAssembly dará como resultado un código que se puede encontrar
aquí .
La función
add1
exportada llama a la función
sum
. No hay implementación de esta función, solo su firma se define en el archivo. Esta es la llamada función externa. Para WebAssembly, esta es una función importada. Su implementación debe ser importada.
Implementamos la función de
sum
usando Go. Para esto necesitamos usar
cgo . Aquí está el código resultante. Algunos comentarios, que son descripciones de los fragmentos de código principales, están numerados. A continuación hablaremos sobre ellos con más detalle.
package main
Analicemos este código:
- La firma de la función
sum
se define en C (consulte el comentario sobre el comando de import "C"
). - La implementación de la función de
sum
se define en Go (observe la línea //export
- este mecanismo cgo usa para establecer la conexión del código escrito en Go con el código escrito en C). NewImports
es una API utilizada para crear importaciones de WebAssembly. En este código, "sum"
es el nombre de la función importada por WebAssembly, sum
es el puntero a la función Go y C.sum
es el puntero a la función cgo.- Y finalmente,
NewInstanceWithImports
es un constructor diseñado para inicializar un módulo WebAssembly con importaciones.
Leer datos de la memoria
La instancia de WebAssembly tiene memoria lineal. Hablemos sobre cómo leer datos de él. Comencemos, como de costumbre, con el código Rust, al que llamaremos
memory.rs
.
#[no_mangle] pub extern fn return_hello() -> *const u8 { b"Hello, World!\0".as_ptr() }
El resultado de compilar este código está en el archivo
memory.wasm
, que se utiliza a continuación.
La función
return_hello
devuelve un puntero a una cadena. La línea termina, como en C, con un carácter nulo.
Ahora ve al lado Ir:
bytes, _ := wasm.ReadBytes("memory.wasm") instance, _ := wasm.NewInstance(bytes) defer instance.Close()
La función
return_hello
devuelve un puntero como un valor
i32
. Obtenemos este valor llamando a
ToI32
. Luego obtenemos los datos de la memoria usando
instance.Memory.Data()
.
Esta función devuelve el segmento de memoria de la instancia de WebAssembly. Puedes usarlo como cualquier rebanada Go.
Afortunadamente, sabemos la longitud de la línea que queremos leer, por lo tanto, para leer la información necesaria, es suficiente usar la construcción de
memory[pointer : pointer+13]
. Luego, los datos leídos se convierten en una cadena.
Aquí hay un ejemplo que muestra mecanismos de memoria más avanzados cuando se usa el código de WebAssembly de Go.
Puntos de referencia
El proyecto go-ext-wasm, como acabamos de ver, tiene una API conveniente. Ahora es tiempo de hablar sobre su desempeño.
A diferencia de PHP o Ruby, el mundo Go ya tiene soluciones para trabajar con código wasm. En particular, estamos hablando de los siguientes proyectos:
- Life from Perlin Network - Intérprete de WebAssembly.
- Go Interpreter's Wagon es un intérprete y kit de herramientas de WebAssembly.
El
material del proyecto php-ext-wasm utilizó el algoritmo
n-body para estudiar el rendimiento. Existen muchos otros algoritmos adecuados para examinar el rendimiento de los entornos de ejecución de código. Por ejemplo, este es el algoritmo de
Fibonacci (versión recursiva) y
el algoritmo Pollard ρ utilizado en Life. Este es el algoritmo de compresión Snappy. Este último funciona con éxito con go-ext-wasm, pero no con Life o Wagon. Como resultado, fue eliminado del conjunto de prueba. El código de prueba se puede encontrar
aquí .
Durante las pruebas, se utilizaron las últimas versiones de los proyectos de investigación. A saber, estos son Life 20190521143330-57f3819c2df0 y Wagon 0.4.0.
Los números que se muestran en la tabla reflejan los valores promedio obtenidos después de 10 inicios de la prueba. El estudio utilizó el MacBook Pro 15 de 2016 "con un procesador Intel Core i7 de 2.9 GHz y 16 GB de memoria.
Los resultados de las pruebas se agrupan a lo largo del eje X de acuerdo con los tipos de pruebas. El eje Y muestra el tiempo, en milisegundos, requerido para completar la prueba. Cuanto más pequeño sea el indicador, mejor.
Comparación de rendimiento de Wasmer, Wagon y Life utilizando implementaciones de varios algoritmosLas plataformas Life y Wagon, en promedio, dan aproximadamente los mismos resultados. Wasmer, en promedio, es 72 veces más rápido.
Es importante tener en cuenta que Wasmer admite tres backends:
Singlepass ,
Cranelift y
LLVM . El backend predeterminado en la biblioteca Go es Cranelift (
aquí puede encontrar más información al respecto). El uso de LLVM proporcionará un rendimiento cercano al nativo, pero se decidió comenzar con Cranelift, ya que este backend proporciona la mejor relación entre el tiempo de compilación y el tiempo de ejecución del programa.
Aquí puede leer sobre diferentes backends, sus pros y sus contras, y en qué situaciones es mejor usarlos.
Resumen
El proyecto de código abierto
go-ext-wasm es una nueva biblioteca Go diseñada para ejecutar código binario wasm. Incluye un
tiempo de ejecución Wasmer . Su primera versión incluye API, cuya necesidad surge con mayor frecuencia.
Las pruebas de rendimiento mostraron que Wasmer, en promedio, es 72 veces más rápido que Life y Wagon.
Estimados lectores! ¿Planea utilizar la capacidad de ejecutar código wasm en Go usando go-ext-wasm?
