Rust Wasmer动态链接库引擎wasmer-engine-dylib的使用,实现高效WebAssembly模块加载与执行

Rust Wasmer动态链接库引擎wasmer-engine-dylib的使用,实现高效WebAssembly模块加载与执行

Wasmer Dylib引擎可以与任何基于wasmer-compiler的编译器实现一起使用,这些编译器能够生成位置无关代码(PIC)。在编译器为函数生成机器代码后,Dylib引擎会生成一个共享对象文件,并通过dlsym进行链接,使其可以被wasmer API使用。

这使得Wasmer能够实现极快的原生启动时间

wasmer-engine-universal的区别

Dylib引擎和Universal引擎主要在模块的加载/存储方式上有所不同。使用相同的编译器时,两者的运行时速度相同。但是,Dylib引擎使用操作系统共享库加载器(通过dlopen),因此在反序列化一个序列化的Module时能够实现更快的启动时间。

要求

wasmer-engine-dylib crate需要在您的系统上安装一个链接器来生成共享对象文件。我们推荐安装gccclang

在Debian-like系统上安装LLVM(提供clang)的命令:

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

在macOS上:

brew install llvm

完整示例代码

以下是一个使用Wasmer Dylib引擎加载和执行WebAssembly模块的完整示例:

use wasmer::{Store, Module, Instance, Value, imports};
use wasmer_engine_dylib::Dylib;

fn main() -> anyhow::Result<()> {
    // 1. 创建一个Dylib引擎的Store
    let engine = Dylib::headless();
    let store = Store::new(&engine);

    // 2. 编译WebAssembly模块
    let wasm_bytes = wat::parse_str(r#"
        (module
            (func $add (param $a i32) (param $b i32) (result i32)
                local.get $a
                local.get $b
                i32.add)
            (export "add" (func $add))
        )
    "#)?;
    
    let module = Module::new(&store, wasm_bytes)?;

    // 3. 创建导入对象(本例中为空)
    let import_object = imports! {};

    // 4. 实例化模块
    let instance = Instance::new(&module, &import_object)?;

    // 5. 获取并调用导出函数
    let add = instance.exports.get_function("add")?;
    let result = add.call(&[Value::I32(1), Value::I32(2)])?;

    // 6. 打印结果
    println!("1 + 2 = {:?}", result[0]);

    Ok(())
}

代码说明

  1. 首先创建一个Dylib引擎的Store实例,这是Wasmer的运行时环境
  2. 使用WAT文本格式定义了一个简单的WebAssembly模块,包含一个加法函数
  3. 将WAT编译为二进制格式,然后编译为可执行的WebAssembly模块
  4. 创建一个空的导入对象(因为我们的模块不需要任何导入)
  5. 实例化模块并获取导出的add函数
  6. 调用函数并打印结果

这个示例展示了如何使用wasmer-engine-dylib快速加载和执行WebAssembly模块。Dylib引擎特别适合需要快速启动WebAssembly模块的场景。


1 回复

Rust Wasmer动态链接库引擎wasmer-engine-dylib的使用指南

概述

wasmer-engine-dylib是Wasmer运行时的一个引擎实现,它通过将WebAssembly模块编译为本地动态链接库(.so/.dylib/.dll)来实现高效的模块加载和执行。这种引擎特别适合需要快速启动和重复执行的场景。

主要特点

  • 快速加载:编译后的模块作为动态库加载,减少重复编译开销
  • 持久化缓存:编译结果可保存为文件,实现跨进程共享
  • 高性能执行:接近原生代码的执行速度
  • 跨平台:支持主流操作系统

安装方法

在Cargo.toml中添加依赖:

[dependencies]
wasmer = { version = "3.0", features = ["dylib"] }
wasmer-engine-dylib = "3.0"

完整示例代码

use wasmer::{Store, Module, Function, Instance, Value, imports};
use wasmer_engine_dylib::Dylib;
use std::fs::File;
use std::io::Write;

fn main() -> anyhow::Result<()> {
    // 1. 创建Dylib引擎实例
    let engine = Dylib::headless();
    let store = Store::new(&engine);

    // 2. 编译WebAssembly模块
    let wasm_bytes = wat::parse_str(r#"
        (module
            (func $add (param $lhs i32) (param $rhs i32) (result i32)
                local.get $lhs
                local.get $rhs
                i32.add)
            (export "add" (func $add))
    "#)?;

    let module = Module::new(&store, wasm_bytes)?;

    // 3. 将模块编译为动态库并保存
    let dylib_bytes = module.serialize()?;
    let mut file = File::create("module.dylib")?;
    file.write_all(&dylib_bytes)?;

    // 4. 从动态库加载模块
    let module = unsafe { Module::deserialize_from_file(&store, "module.dylib") }?;

    // 5. 实例化并执行模块
    let import_object = imports! {};
    let instance = Instance::new(&module, &import_object)?;

    let add: Function = instance.exports.get_function("add")?;
    let result = add.call(&[Value::I32(1), Value::I32(2)])?;

    assert_eq!(result[0], Value::I32(3));
    println!("1 + 2 = {}", result[0].i32().unwrap());

    Ok(())
}

高级用法示例

自定义缓存路径和共享缓存

use wasmer_engine_dylib::DylibEngineConfig;

fn advanced_usage() -> anyhow::Result<()> {
    // 配置自定义缓存路径
    let config = DylibEngineConfig::new()
        .cache_path("/tmp/wasmer_cache");
    
    let engine = Dylib::new(config);
    let store = Store::new(&engine);

    // 编译并保存模块
    let wasm_bytes = wat::parse_str(r#"(module)")?;
    let module = Module::new(&store, wasm_bytes)?;
    module.serialize_to_file("/tmp/wasmer_cache/module.dylib")?;

    // 另一个进程可以共享这个缓存
    let config = DylibEngineConfig::new()
        .cache_path("/tmp/wasmer_cache");
    
    let engine = Dylib::new(config);
    let store = Store::new(&engine);
    
    let _module = unsafe { Module::deserialize_from_file(&store, "/tmp/wasmer_cache/module.dylib") }?;

    Ok(())
}

性能建议

  1. 批量编译:对于多个WASM模块,提前编译为动态库
  2. 共享缓存:在多进程环境中使用共享缓存目录
  3. 预热:在系统启动时加载常用模块
  4. 长期运行:适合长时间运行的服务,而非一次性脚本

注意事项

  • 动态库格式是平台相关的,需要在目标系统上编译
  • 需要处理动态库加载的安全问题(使用unsafe
  • 动态库可能比原始WASM文件大很多
  • 确保动态库文件权限设置正确

wasmer-engine-dylib为需要高性能WASM执行的场景提供了优秀的解决方案,特别适合服务端应用和频繁调用的模块。

回到顶部