首页 > 其他分享 >WebAssembly初尝试

WebAssembly初尝试

时间:2023-04-14 16:55:33浏览次数:72  
标签:尝试 WebAssembly webassembly module wasm 文件 test

前言

  • 之前老是听别人提到WebAssembly这个词,一直对其比较模糊,不能理解是个啥东西,后来自己实践了一下,发现其实就是一种提高代码性能的手段。

简介

  • WebAssembly 是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如 C、C++和 Rust 等低级源语言提供一个高效的编译目标。(解释来自MDN)
  • 通俗一点来讲,就是利用一些C、C++、Rust等偏底层的一些语言去实现部分功能并编译成二进制文件然后暴露给第三方平台(可能是浏览器端,也可能是服务端,还有可能是客户端),可以丰富和优化相关的应用。
  • 当然了,其实也有类似asm.js这种非底层语言去实现,但是它编译后的二进制文件减少了编译的过程,其实也是可以提高代码的性能。

开始尝试

准备工作

开始

  • 使用上面安装好的wasm-pack新建一个空项目,wasm-pack new test-webassembly
  • 查看目录结构

    test-webassembly

    src

    lib.rc // 主入口
    utils.rs // 依赖的工具方法

    tests

    web.rs // 测试文件

    .appveyor.yml // 项目的前置依赖配置
    .gitignore // git忽略的文件配置
    .travis.yml // 该项目的CI环境配置文件
    cargo.toml // 该项目的配置文件,类似package.json
    README.md // 解释文件

重点文件分析

  • lib.rs,默认自带的这个文件只实现了一个greet方法,并暴露给了浏览器端,里面调用了alert方法。
    use wasm_bindgen::prelude::*; // 引入wasm_bindgen::prelude下所有的类和方法
    
    #[cfg(feature = "wee_alloc")]
    #[global_allocator]
    static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; // 一般不需要用,可以选择注释
    
    #[wasm_bindgen]
    extern {
        fn alert(s: &str); // 表示第三方环境可以直接用的方法定义,编译的时候会直接去调用第三方环境中的同名方法
    }
    
    #[wasm_bindgen] // 类似ts中的装饰器,打上这个标签,表示是需要与第三方环境去交互的,通俗一点,就是这个地方会被打包到第三方环境中。
    pub fn greet() {
        alert("Hello, test-webassembly!");
    }
    
  • Cargo.toml,这个文件主要就是一些项目配置信息了。
    [package]
    name = "test-webassembly" // 包名
    version = "0.1.0" // 包版本
    authors = ["xxx"] // 作者名
    edition = "2018" // rust版本
    
    [lib] // target设置,类似webpack种的target,将其打包成什么样的包
    crate-type = ["cdylib", "rlib"] // 分别代表动态系统库和rust静态库
    
    [features] // 用来条件编译
    default = ["console_error_panic_hook"] // 这个可以删掉不需要
    
    [dependencies]
    wasm-bindgen = "0.2.63" // 这个就是主要的处理rust代码转化成webassembly的包
    
    # The `console_error_panic_hook` crate provides better debugging of panics by
    # logging them with `console.error`. This is great for development, but requires
    # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
    # code size when deploying.
    # console_error_panic_hook = { version = "0.1.6", optional = true } 这个其实如果不是特别需要也可以不用
    
    # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
    # compared to the default allocator's ~10K. It is slower than the default
    # allocator, however.
    # wee_alloc = { version = "0.4.5", optional = true } 这个一般用不上,注释掉就可以
    
    [dev-dependencies]
    wasm-bindgen-test = "0.3.13" // 用来测试的包,自己写一般可以不用
    
    [profile.release]
    # Tell `rustc` to optimize for small code size.
    opt-level = "s"
    

改造

接下来实现一个简单的图片处理工具,帮助web端更快的处理图片内容。

  1. 会用到image这个rust包。所以先改造它的Cargo.toml
[dependencies]
wasm-bindgen = "0.2.63"
// 在之前的dependencies下面添加image包
image = "0.24.6"
  1. 就以处理图片的其中内置的一个灰度方法来看一下。接下来改造lib.rs文件。
// 新增一个灰度方法,接受一个字节类型的集合,返回一个新的字节类型的集合
#[wasm_bindgen]
pub fn gray(_array: &mut [u8]) -> Vec<u8> {
    let mut img = image::load_from_memory(_array).unwrap(); // 将传入的u8集合转化成一个image对象
    img = img.grayscale(); // 调用第三方包的灰度方法得到新的图片
    let mut bytes: Vec<u8> = Vec::new(); // 定义一个新的u8集合
    img.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png).unwrap(); // 将变换后的image转化成新的u8集合
    bytes // 将u8集合作为返回值抛出
}
  1. 运行命令wasm-pack build --release --target web,将rust文件打包成目标为web项目的可用二进制文件以及加载的js文件。可以看到项目多了一个pkg文件夹

    pkg

    .gitignore
    package.json
    README.md
    test_webassembly_bg.wasm // 二进制文件
    test_webassembly_bg.wasm.d.ts
    test_webassembly.d.ts
    test_webassembly.js // 这个就是加载这个二进制的文件

// test_webassembly.js这个文件主要看几个重点地方,其实不看也行,这个地方wasm-pack已经帮你处理好了,基本只需要学会用就行
// 加载二进制后的处理,返回暴露出的模块的实例
async function load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
      // 在支持instantiateStreaming的情况下,优先使用instantiateStreaming加载对应的二进制文件
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports); 
            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
                } else {
                    throw e;
                }
            }
        }

        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);

    } else {
      // 不支持的时候,就使用instantiate文件来加载
        const instance = await WebAssembly.instantiate(module, imports);

        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };

        } else {
            return instance;
        }
    }
}
// 针对已经解析完的实例,做一些初始化操作,抛出模块实例的exports
function finalizeInit(instance, module) {
    wasm = instance.exports;
    init.__wbindgen_wasm_module = module;
    cachedInt32Memory0 = null;
    cachedUint8Memory0 = null;


    return wasm;
}
// 文件暴露的入口
async function init(input) {
    if (typeof input === 'undefined') {
        input = new URL('test_webassembly_bg.wasm', import.meta.url); // 默认的二进制文件地址
    }
    const imports = getImports();

    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input); // 加载对应的二进制文件
    }

    initMemory(imports); // 这个就是之前上面申请空间的,可以注释掉的,不用看

    const { instance, module } = await load(await input, imports); // 调用webassembly的api去加载二进制文件

    return finalizeInit(instance, module); // 得到模块中抛出的exports
}

在html中使用,新建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="file" multiple="false" id="file">

    <div id="box"></div>
    <div>
        <button id="gray-btn">灰度</button>
    </div>
    <div id="target"></div>

    <script type="module">
        import init, { gray } from './pkg/test_webassembly.js';

        await init();

        const file = document.getElementById('file');
        const grayBtn = document.getElementById('gray-btn');
        let current = null;
        let uint8Array = [];
        file.addEventListener('change', (e) => {
            current = e.target.files[0];
            var reader = new FileReader(); 
            reader.readAsDataURL(current);
            reader.onload = function() {
                const img = new Image();
                img.src = this.result;
                document.querySelector('#box').appendChild(img);
            }
        });
        grayBtn.addEventListener('click', () => {
            var reader = new FileReader();
            reader.readAsArrayBuffer(current);
            reader.onload = function () {
                uint8Array = new Uint8Array(this.result);
                let newUnit8Array = gray(uint8Array); // 调用webassembly提供的灰度方法获取到灰度之后的结果

                const img = new Image();
                let blob = new Blob([newUnit8Array], { type: 'image/png' });
                img.src = window.URL.createObjectURL(blob);
                document.querySelector('#target').appendChild(img);
            }
        })
    </script>
</body>
</html>

使用npx http-server --port 3000启动静态服务器,因为webassembly是不支持本地的文件的,下面就是最终效果,先选择一张图片,之后点击灰度就能快速得到一张灰度的图片

小结

  • WebAssembly是一个很有意思的技术方向,它为web应用提供了很多可能,图片处理,视频解析、加密等等一些复杂的场景都可以变得更轻便化。

标签:尝试,WebAssembly,webassembly,module,wasm,文件,test
From: https://www.cnblogs.com/aloneMing/p/17318823.html

相关文章

  • 对于任何事情,敢尝试就离成功更近了一步。
     测试是什么?对于一个五年没碰过书,没接触这一行的我来说,真的完全不懂。我的经历要从当兵说起。年少时,我响应国家号召应征入伍,在部队接触了很多人,自己也有很多改变。两年后退伍,我却不知道该如何立足社会。第一份工作是加入某小区特警队,早上准时起床跑步,白天不出警就去体育馆搞......
  • new bing侧边栏撰写功能尝试--STM32的使用
    生成结果如下:stm32的使用stm32是一种基于ARMCortex-M内核的32位微控制器,由意法半导体(ST)推出。stm32具有超低的价格、超多的外设、丰富的型号、优异的实时性、极低的开发成本等优势,是目前市场上最受欢迎的单片机之一。本文将介绍stm32的基本特点和使用方法,帮助新手入门stm32的......
  • WebAssembly 助力云原生:APISIX 如何借助 Wasm 插件实现扩展功能?
    本文将介绍Wasm,以及ApacheAPISIX如何实现Wasm功能。作者朱欣欣,API7.ai技术工程师原文链接什么是WasmWasm是WebAssembly的缩写。WebAssembly/Wasm是一个基于堆栈的虚拟机设计的指令格式。在Wasm未出现之前,浏览器中只能支持运行Javascript语言。当Wasm出现......
  • 1661_MIT 6.868 JOS page_init实现的初步尝试
    GreyZhang/g_unix:somebasiclearningaboutunixoperatingsystem.(github.com)        最近的几次学习笔记的整理都有一点拿不准,不是很确定自己所做的是否准确。不过,如果战线跨得太长,可能整个新建立的知识体系会垮掉。因此,还是一步步做梳理,做记录。即便是后面发现了......
  • 尝试Questa仿真报错:Error while trying to run Questa simulator
        最近在看一些芯片验证的书籍,逐步学习数字芯片的一些测试原理。以前的混合芯片测试,大多不需要了解其内部的具体原理,很多情况下,了解基本的I/O结构和通讯方式即可。但想更进一步学习,无论如何都避不开verilog了。从SSI,MSI,LSI,VLSI到ULSI,再到如今的Soc,数字电路规模的进步速......
  • WebAssembly逆向
    一、WebAssembly简介WebAssembly是一种可以使用非JavaScript编程语言编写代码,并且能在浏览器上运行的技术方案。借助Emscripten编译工具,能将C/C++文件转成wasm格式,JavaScript可以直接调用该文件并执行其中的方法。好处可以隐藏核心逻辑,增大逆向难度提高执行效率(基于C/......
  • 基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试 - 3/3
    基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-1/3基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-2/3基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-3/3项目地址:https://github.com/janrs-io/Jgrpc转载请注明来源:https://janrs.com/6rdh......
  • 基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试 - 2/3
    基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-1/3基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-2/3基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试-3/3项目地址:https://github.com/janrs-io/Jgrpc转载请注明来源:https://janrs.com/ugj7......
  • 类似ChatGPT的开源项目ChatGLM-6B安装尝试
    项目git地址https://github.com/THUDM/ChatGLM-6B配置要求:本机电脑显卡显存6G以上。只有cpu则需要32GB以上。安装基础环境1.安装python3.10版本https://www.python.......
  • 新建 Blazor 项目 WebAssembly
           ......