惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
C
Check Point Blog
B
Blog RSS Feed
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
About on SuperTechFans
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 聂微东
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
小众软件
小众软件
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
GbyAI
GbyAI
The Cloudflare Blog
博客园 - 叶小钗
S
SegmentFault 最新的问题
博客园 - Franky
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
B
Blog
Jina AI
Jina AI

博客园 - Artech

[MAF预定义的AIContextProvider-05]CompactionProvider——采用多种策略压缩对话历史 [MAF预定义的AIContextProvider-04]Mem0Provider——长期记忆基于的云端解决方案 [MAF预定义的AIContextProvider-03]ChatHistoryMemoryProvider——赋予Agent从经验中学习的能力 [MAF预定义的AIContextProvider-02]AgentSkillsProvider——将Agent Skills引入MAF [MAF预定义的AIContextProvider-01]TextSearchProvider——RAG在MAF中的实现 [MAF预定义ChatClient中间件-09]MessageInjectingChatClient-赋予工具消息注入的能力 [MAF预定义ChatClient中间件-08]OpenTelemetryChatClient-实现链路跟踪和性能监控 [MAF预定义ChatClient中间件-07]PerServiceCallChatHistoryPersistingChatClient——基于ReAct循环的一步一存档 [MAF预定义ChatClient中间件-06]利用ImageGeneratingChatClient开发专业图片生成Agent [MAF预定义ChatClient中间件-05]动态修改ChatOptions和请求消息 [MAF预定义ChatClient中间件-04]ReducingChatClient——精减对话历史又不丢失基本语义 [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间 [MAF预定义ChatClient中间件-02]FunctionInvokingChatClient——实现ReAct循环和人机交互的大功臣 [MAF预定义ChatClient中间件-01]LoggingChatClient——在调用LLM前后输出日志 [MAF的Agent管道详解-07]利用AIAgent中间件构建Agent管道 [MAF的Agent管道详解-06]ChatClientAgent对IChatClient和输入输出增强管道的整合 [MAF的Agent管道详解-06]ChatClientAgent对IChatClient和输入输出增强管道的整合 - Artech [MAF的Agent管道详解-05]对话历史的持久化和输入输出的增强 [MAF的Agent管道详解-04]如何让LLM按照要求的结构输出数据? [MAF的Agent管道详解-03]连接LLM的IChatClient对象 [MAF的Agent管道详解-02]IChatClient管道如何完美连接大模型? [MAF的Agent管道详解-01]塑智能体边界,从AIAgent抽象类开始 [对比学习LangChain和MAF-04]针对消息的设计 [对比学习LangChain和MAF-03]完全不同的Agent设计哲学 [对比学习LangChain和MAF-02]基本编程模式的差异(下篇) [对比学习LangChain和MAF-01]基本编程模式的差异(上篇) 这是一篇测试文章 我所理解的Python元模型 除了按值和引用,方法参数的第三种传递方式 方法的三种调用形式
WebAssembly核心编程[1]:wasm模块实例化的N种方式
Artech · 2024-01-31 · via 博客园 - Artech

当我们在一个Web应用中使用WebAssembly,最终的目的要么是执行wasm模块的入口程序(通过start指令指定的函数),要么是调用其导出的函数,这一切的前提需要创建一个通过WebAssembly.Instance对象表示的wasm模块实例(源代码)。

一、wasm模块实例化总体流程
二、利用WebAssembly.Module创建实例
三、通过字节内容创建创建实例
四、利用XMLHttpRequest加载wasm模块
五、极简编程方式

一、wasm模块实例化总体流程

虽然编程模式多种多样,但是wasm模块的实例化总体采用如下的流程:

  • 步骤一:下载wasm模块文件;
  • 步骤二:解析文件并创建通过WebAssembly.Module类型表示的wasm模块;
  • 步骤三:根据wasm模块,结合提供的导入对象,创建通过WebAssembly.Instance类型表示的模块实例。

二、利用WebAssembly.Module创建实例

我们照例通过一个简单的实例来演示针对wasm模块加载和模块实例创建的各种编程模式。我们首先利用WebAssembly Text Format(WAT)形式定义如下一个wasm程序,定义的文件名为app.wat。如代码所示,我们定义了一个用于输出指定浮点数(i64)绝对值的导出函数absolute。绝对值通过f64.abs指令计算,具体得输出则通过导入的print函数完成。

(module
   (func $print (import "imports" "print") (param $op f64) (param $result f64))
   (func (export "absolute") (param $op f64)
      (local.get $op)
      (f64.abs (local.get $op))
      (call $print)
   )
)

我们通过指定wat2wasm (源代码压缩包种提供了对应的.exe)命令(wat2wasm app.wat –o app.wasm)编译app.wat并生成app.wasm后,定义如下这个index.html页面,作为宿主程序的JavaScript脚本完全按照上面所示的步骤完成了针对wasm模块实例的创建。

<html>
    <head></head>
    <body>
        <div id="container"></div>
        <script>
            var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
           fetch("app.wasm")
                .then((response) => response.arrayBuffer())
                .then(bytes => {
                    var module = new WebAssembly.Module(bytes);
                    var instance = new WebAssembly.Instance(module, {"imports":{"print": print}});
                    instance.exports.absolute(-3.14);
                })
        </script>
    </body>
</html>

具体来说,我们调用fetch函数将app.wasm文件下载下来后,我们将获得的字节内容作为参数调用构建函数创建了一个WebAssembly.Module对象。然后将这个Module对象和创建的导入对象({"imports":{"print": print}})作为参数调用构造函数创建了一个WebAssembly.Instance对象,该对象正是我们需要的wasm模块实例。我们从模块实例中提取并执行导出的absolute函数。导入的print函数会将绝对值计算表达式以如下的形式输出到页面中。

image

除了调用构造函数以同步(阻塞)的方式根据WebAssembly.Module对象创建WebAssembly.Instance对象外,我们还可以调用WebAssembly.instantiate静态方法以异步的方式“激活”wasm模块实例,它返回一个Promise<WebAssembly.Instance>对象。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
fetch("app.wasm")
    .then((response) => response.arrayBuffer())
    .then(bytes => {
        var module = new WebAssembly.Module(bytes);
        return WebAssembly.instantiate(module, { "imports": { "print": print } });
    })
    .then(instance => instance.exports.absolute(-3.14));

三、通过字节内容创建创建实例

静态方法WebAssembly.instantiate还提供了另一个重载,我们可以直接指定下载wasm模块文件得到的字节内容作为参数。这个重载返回一个Promise<WebAssembly.WebAssemblyInstantiatedSource>对象,WebAssemblyInstantiatedSource对象的instance属性返回的正是我们需要的wasm模块实例。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
fetch("app.wasm")
    .then((response) => response.arrayBuffer())
    .then(bytes => WebAssembly.instantiate(bytes, {"imports":{"print": print}}))
    .then(result =>result.instance.exports.absolute(-3.14));

四、利用XMLHttpRequest加载wasm模块

fetch函数是我们推荐的用于下载wasm模块文件的方式,不过我们一定义要使用传统的XMLHttpRequest对象也未尝不可。上面的三种激活wasm模块实例的方式可以采用如下的形式来实现。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
const request = new XMLHttpRequest();
request.open("GET", "app.wasm");
request.responseType = "arraybuffer";
request.send();

request.onload = () => {
    var bytes = request.response;
    var module = new WebAssembly.Module(bytes);
    var instance = new WebAssembly.Instance(module, {"imports":{"print": print}});
    instance.exports.absolute(-3.14);
};

上面演示的利用创建的WebAssembly.Module对象和导入对象调用构造函数创建WebAssembly.Instance的同步形式。下面则是将二者作为参数调用静态方式WebAssembly.instantiate以异步方式激活wasm模块实例的方式。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
const request = new XMLHttpRequest();
request.open("GET", "app.wasm");
request.responseType = "arraybuffer";
request.send();

request.onload = () => {
    var bytes = request.response;
    WebAssembly
        .instantiate(request.response, {"imports":{"print": print}})
        .then(result => result.instance.exports.absolute(-3.14));
};

下面演示WebAssembly.instantiate静态方法的另一个重载。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
const request = new XMLHttpRequest();
request.open("GET", "app.wasm");
request.responseType = "arraybuffer";
request.send();

request.onload = () => {
    var bytes = request.response;
    WebAssembly
        .instantiate(request.response, {"imports":{"print": print}})
        .then(result => result.instance.exports.absolute(-3.14));
};

五、极简编程方式

其实我们有“一步到位”的方式,那就是按照如下的形式执行静态方法WebAssembly.instantiateStreaming。该方法的第一个参数用于提供下载.wasm模块文件的PromiseLike<Response>对象,第二个参数则用于指定导入对象。该方法同样返回一个Promise<WebAssembly.WebAssemblyInstantiatedSource>对象,WebAssemblyInstantiatedSource的instance属性返回的正是我们所需的wasm模块实例。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
WebAssembly
    .instantiateStreaming(fetch("app.wasm"), {"imports":{"print": print}})
    .then(result => result.instance.exports.absolute(-3.14))