webassembly学习

webassembly学习

基本理论

webassembly介绍

产生的原因:希望将一些非javascript的代码运行在浏览器上,避免重新开发的工作量。
webassembly诞生
在这里插入图片描述

wasm介绍

系统学习webassembly
webassembly实现细节
为什么webassembly是web的未来?

基本信息
  • wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式.
  • wasm 代码格式: - .wasm - wasm二进制格式
  • .wabt - web assembly text format,编译结果的文本格式,用于调试
  • 通过wabt可以实现wasm和wabt格式的转换
为什么需要webassembly

1)javascript的执行流程:
在这里插入图片描述

  • 拿到了 JS 源代码,交给 Parser,生成 AST
  • ByteCode Compiler 将 AST 编译为字节码(ByteCode)
  • ByteCode 进入翻译器,翻译器将字节码一行一行翻译(Interpreter)为机器码(Machine Code),然后执行
    存在问题:函数多次调用,针对静态变量,经过JIT后生成机器语言,不需要经过 Parser-Compiler-Interpreter 这个过程。而javascript是动态类型语言,对于类型和参数个数的变化,则需要重新经过Parser-Compiler-Interpreter。

2)ASM.js的出现

  • 静态的编译语言,严格要求变量的类型在运行时确定且不可改变,且去除了 JavaScript 拥有的垃圾回收机制,需要开发者手动管理内存。
    但是不管 asm.js 再怎么静态化,干掉一些需要耗时的上层抽象(垃圾收集等),也还是属于 JavaScript 的范畴,代码执行也需要 Parser-Compiler 这两个过程,而这两个过程也是代码执行中最耗时的。

3)webassembly的出现

  • 直接和 Machine Code 打交道的汇编语言 WebAssembly
  • 直接干掉 Parser-Compiler,同时 WebAssembly 是一门强类型的静态语言,能够进行最大限度的 JIT 优化,使得 WebAssembly 的速度能够无限逼近 C/C++ 等原生代码。

总的来说:webassembly一方面可以支持c/c++编译为javascript,另一方面在性能方面更好。

ASM.js(wasm的前身)

ASM.js仍然是被编译出来的,书写的C/C++代码经过静态编译后,会输出为一段特定的JavaScript代码,浏览器引擎在运行这段代码时,会做特别的优化。
阮一峰的入门

javascript是动态语言,当多次调用同一个函数,参数类型发生变化后,需要重新进行parser-compile。而Asm.js是静态编译语言,会更快,去除了Javascript垃圾回收机制。详细可看:
为什么webassembly是web的未来?

wasm会替换javascript么

wasm不会替换javascript,可以帮助javascript解决一些无法解决的问题

  • 解除对不同浏览器环境的强依赖

如今现代前端开发对webpack、babel、polyfill的依赖都非常重,编译后动辄就是几兆几兆的大小,当一个业务非常复杂之后,不得不采用各种各样的手段来做优化。另外,浏览器的安全也是一个大问题。

  • 弱类型还是强类型

尽管我们有TypeScript,然而它只能在编译阶段帮我们做好类型的校验,除此之外,如 + 运算符、null == undefined 等诡异结果也是JavaScript种种难以克服的通病。

  • WASM 的设计初衷就可以梳理为以下几点:
    1)最大程度的复用现有的底层语言生态,如 C/C++ 在游戏开发、编译器设计等方面的积淀
    2)在 Web、Node.js 或其他 WASM runtime 获得近乎于原生的性能,也就是可以让浏览器也能跑大型游戏、图像剪辑等应用
    3)还有最大程度的兼容 Web、保证安全
    4)同时在开发上(如果需要开发)易于读写和可调试,这一点 AssemblyScript 走得更远
    所以从初衷出发,WebAssembly 的作用更适合下面这张图:
    在这里插入图片描述

WASM 桥接各种系统编程语言的生态,进一步补齐了 Web 开发生态之外,还为 JS 提供性能的补充,正是 Web 发展至今所缺失的重要的一块版图。

一种直接运行在web前端,一种运行在后端服务,通过wasm runtime来启动

将 WebAssembly 作为编程语言的一种尝试

因为上述的二进制和文本格式都不适合编码,所以不适合将 WASM 作为一门可正常开发的语言。

为了突破这个限制,AssemblyScript[6] 走到台前,AssemblyScript 是 TypeScript 的一种变体,为 JavaScript 添加了 WebAssembly 类型[7] , 可以使用 Binaryen[8] 将其编译成 WebAssembly。

目前使用 AssemblyScript 构建的应用生态已经初具繁荣,目前在区块链、构建工具、编辑器、模拟器、游戏、图形编辑工具、库、IoT、测试工具等方面都有大量使用 AssemblyScript 构建的产物:https://www.assemblyscript.org/built-with-assemblyscript.html#games

wasm应用场景

在这里插入图片描述

wasm运行原理
  • 如何运行的
    一般来说,一个可运行的程序分为两部分,即数据 + 指令,而WASM是基于堆栈机模型和虚拟指令集来实现程序的运行,堆栈机模型是常见的内存管理结构,将数据和操作符压入栈中并弹出逐次执行,而虚拟指令集(V-ISA)则可以认为是对平台无关的一系列自定义操作符(如JVM),相对的,物理指令集(ISA)则是强依赖物理系统的(如Intel的x86-64)。
    我们把浏览器比作JVM虚拟机,WASM二进制编码比作Java字节码,是不是就能很快明白为什么浏览器上可以运行WASM了?
  • 剖析WASM文件结构
    首先在组织结构上,WASM会把特定功能或者有相关联的代码放进一个特定的区块(Section)中,而这些区块(Section)组成了程序。
    1)TypeSection
    存放与“类型”相关的内容,主要是“函数签名”,即返回值与参数值。
    2)StartSection
    在模块初始化完成后,被首先调用的函数,可以近似理解为main函数
    3)GlobalSection
    顾名思义,存放了程序相关的全局变量,可能是程序自定义的数据,也可能是流程相关的
    4)CustomSection
    可以用作将来自定义扩展功能的实现
    5)ImportSection
    从宿主环境中,我们可以向WASM模块导入各式各样的数据,换句话说,通过这个Section我们可以实现数据或代码的共享
    6)ExportSection
    同理,既然可以导入,我们也可以导出数据和方法
    7)FunctionSection
    这里存储的是函数类型,与TypeSection一一对应
    8)CodeSection
    这里存储的是函数具体代码,与FunctionSection一一对应
    9)TableSection、ElementSection
    Table存放了函数指针的元信息,Element则是对应Table的具体内容,关于Table的概念可以参考这篇文章:https://zhuanlan.zhihu.com/p/28222049
    10)MemorySection、DataSection
    同上,Memory描述了使用内存的基本情况,Data则是对应具体的实际内容
    wasm以 asm这个字符串的二进制编码开头,随后跟上版本号。
  • 基本数据类型
    1)无符号整数
    WASM支持三种非负整数类型: uint8、uint16、uint32,后面的数字表示占用了多少个bit
    2)可变长无符号整数
    WASM支持三种可变长非负整数类型: varuint1, varuint7, varuint32,所谓可变长的意思是会根据具体数据大小决定使用多少bit,后面的数字表示最大可占用多少个bit
    3)可变长有符号整数
    同上,这里允许负数的出现,这里支持varint7, varint32, varint64三种类型
    4)浮点数
    这里同JS,采用IEEE-754方案,单精度为32位
周边生态
  • 编译工具
    Emscripten、LLVM、Binaryen

  • WAT 使WASM更加可读
    通常来说,WASM二进制文件是不可读的,WebAssembly Text Format(WAT)是另外一种输出格式,以类文本的方式展示输出,我们可以近似的理解为与二进制等价的汇编语言,或者说就是WASM的source-map。
    另外,Flat-WAT是经过优化后的WAT格式,我们可以一张图比较三者输出:
    在这里插入图片描述
    现在社区已经有成熟的转换工具,如wasm2wat,wat2wasm等,我们可以在一个名为WABT(WebAssembly Binary Toolkit)的工具集中找到。

webassembly云原生

wasm云原生

  • wasi给webasembly提供了在操作系统运行的接口,由wasmtime项目设计,可提供对几种类似操作系统的功能访问,包括文件和文件系统,Berkeley套接字,时钟和随机数。
  • wasm在runtime的沙箱机制中运行,沙箱提供了了系统的隔离性和安全性

webassembly会替代docker么
webassembly具有更轻量、性能更高、安全的特点,冷启动更快。适用于docker很难满足的边缘计算、区块链、汽车等场景。而docker对于高并发高稳定性的服务端应用更具有优势。

wasi介绍

WASI是一个新的API体系, 由Wasmtime项目设计, 目的是为WASM设计一套引擎无关(engine-indepent), 面向非Web系统(non-Web system-oriented)的API标准. 目前, WASI核心API(WASI Core)在做覆盖文件,网络等等模块的API, 但这些实现都是刚刚开始实现, 离实用还是有很长路要走。

 本质让wasm可以在非web系统上运行,把Wasm模块放进Runtime提供的沙箱中运行,Runtime可以限制模块的对于操作系统的访问,为其提供有限的文件系统访问等。而Runtime可以被设计成跨平台的,我们可以在Linux/Windows/MacOS/嵌入式等平台上运行同一个Wasm模块。这就相当于我们有了跨平台且安全的二进制执行方法。
wasm+wasi(服务端)

运行在服务端,类似docker
场景:对于需要频繁启动和停止的场景,会更有优势,服务端docker一旦启动,资源就会一直占用,对于需要高速响应,但是请求却不是很持续。
wasm云原生

wasm runtime对比(虚拟机)

参考资料:
四种主流wasm

在这里插入图片描述

对比
在这里插入图片描述

wasmedge

wasmedgr

wasmtime

WASMTIME是字节码联盟主推的一个WASM虚拟机,既可以作为一个CLI,也可以被嵌入到其他应用系统中,如IoT或者云原生
wasmtime编译cpython
wasmtime

wasmer

这是独立于字节码联盟,并努力构建自己生态的社区推出的产品,特点是支持在更多的编程语言运行WASM实例,并有自己的包管理平台Wapm
wasmer

wamr

同样是字节码联盟旗下的,更偏向于芯片场景的虚拟机,如它的名字所示,体积非常小,起步速度只要100微秒,内存耗费最低只需100KB
wamr

wavm

wavm
WAVM是WebAssembly虚拟机,设计用于非Web应用程序。
特点

  • 快速:WAVM使用LLVM将WebAssembly代码编译为具有接近本机性能的机器代码。在某些情况下,它甚至可以胜过本机性能,这要归功于它能够生成针对运行代码的确切CPU进行了调整的机器代码。
  • 安全:WAVM阻止WebAssembly代码访问WebAssembly虚拟机*之外的状态,或调用未与WebAssembly模块明确链接的本机代码。
https://github.com/WAVM/WAVM
wavm run helloworld.wast
wavm run zlib.wasm
wavm run trap.wast
wavm run echo.wast "Hello, world!"
wavm run helloworld.wast | wavm run tee.wast
wavm run --enable simd blake2b.wast

emscripten教程

参考资料:
emscripten中文
emscriten英文

TODO
注意

Mozilla Firefox具有4 GB内存限制; Google Chrome具有1.4 GB内存限制。 Internet Explorer 11具有1 GB内存限制,360浏览器也具有1G的限制。超过内存限制,wasm会被截断,加载不了。

模型推理

paddlejs

paddlejs垃圾分类
paddlejs

paddlejs小程序跑模型
  • 第一步:准备一个paddlepaddle模型
    模型使用的是链接中的模型:
    导出的paddle模型
  • 第二步:模型转换
pip install paddlejsconverter

paddlejsconverter --modelPath=./model.pdmodel --paramPath=./model.pdiparams --outputDir=./ --useGPUOpt=True

paddle导出的模型转换后,会报错,issue有人提出,等待解决:

webgl createProgram: conv2d -- Error: Error: compile: ERROR: 0:186: 'undefined' : undeclared identifier
ERROR: 0:186: '' : boolean expression expected

先使用:
转换好的模型

  • 第三步:准备微信小程序环境
    工程可以直接拉大佬的代码:小程序工程。以下代码只是重要部分,不全。
    1)下载微信小程序开发工具:
    微信小程序
    2)新建项目, appID需要注册,测试号使用不是特别方便,需要用申请到的appid创建项目,且选择不使用云开发 。
    微信小程序账号申请
    如果新建项目时用的游客,可通过微信小程序开发工具(详情–>基本信息修改appid)
    3)添加paddlejs插件。位置:先登录微信公众号平台,设置–>第三方设置–>插件管理–>添加插件,搜索:wx7138a7bb793608c3.
    添加这个插件是为了给appid账号授权paddlejs。
    4)在小程序的app.json 中声明插件的provider(appid)& version(指定版本号)
{
...
"plugins": {
 "paddlejs-plugin": {
   "version": "2.0.1",
   "provider": "wx7138a7bb793608c3"
 }
}
...
}

5)进入小程序项目根目录(可以通过右键,通过资源管理器打开进入或者知道文件创建的路径)
在这里插入图片描述
6)使用npm init -yes 创建初始化,此时会出现一个package.json(如果没有npm需要去安装nodejs)。
7)使用npm包引入@paddlejs/paddlejs-core与@paddlejs/paddlejs-backend-webgl,具体如下
在package.json中添加或修改如下

{

“dependencies”: {
“@paddlejs/paddlejs-backend-webgl”: “^1.0.7”,
“@paddlejs/paddlejs-core”: “^2.0.7”
}
}

回到小程序根目录,执行npm i。
8)使用小程序开发者工具,点击 工具-》构建npm!!!
同时关闭合法域名校验(右边详情 -》 本地设置 -》 不校验合法域名…)!!!
在这里插入图片描述
在这里插入图片描述
到这一步,基本上环境是搭建好了,准备使用

9)在app.js或在小程序page的js文件中引入 @paddlejs/paddlejs-core、@paddlejs/paddlejs-backend-webgl 并初始化

import * as paddlejs from '@paddlejs/paddlejs-core';
import '@paddlejs/paddlejs-backend-webgl';

const plugin = requirePlugin("paddlejs-plugin");
plugin.register(paddlejs, wx);

export const PaddleJS = new paddlejs.Runner({
  modelPath: 'http://127.0.0.1', //你本地的ip地址也可以用
  feedShape: {
      fw: 224,
      fh: 224
  },
  fill: '#fff',
  targetSize: {
      height: 224,
      width: 224
  },
  bgr: false,
  mean: [0.485, 0.456, 0.406],
  std:  [0.229, 0.224, 0.225],
  needPreheat: true,
  webglFeedProcess: true
});

10)现在拿出你转换后的模型,进入目录,使用python -m http.server 80进行挂载,这样可以通过127.0.0.1进行访问 这也是为什么modelPath填写127.0.0.1的原因,你可以测试一下,现在是否能通过127.0.0.1/model.json访问model.json
11)如果初始化成功,那么就剩下预测的代码了。

test: function(){
    wx.chooseImage({
      count: 1,
      sizeType: ['original'],
      sourceType: ['album', 'camera'],
      success:res => {
        console.log(res)
        this.setData({
          imageSrc: res.tempFilePaths[0]      
        })

        wx.getImageInfo({
          src: res.tempFilePaths[0],
          success: res=> {

            let {width, height, path} = res;
            
            const ctx = wx.createCanvasContext("myCanvas")
            console.log(ctx)
            ctx.drawImage(path, 0, 0, width, height);
            ctx.draw(false, () => {

              wx.canvasGetImageData({
                canvasId: "myCanvas",
                height: height,
                width: width,
                x: 0,
                y: 0,
                success: res => {

                  PaddleJS.predict(res).then(res =>{
                    console.log(res)
                    const max = Math.max.apply(null, res);
                    const index = res.indexOf(max);
                    console.log(index, max)
                    console.log(labelMap[index])
                    
                  })
                },
                fail: res => {
                  console.log(res)
                }
              },)
            })
          }
        })
      }
    }) 
  },

12)最后编译,可以在模拟器中查看和调用。
在这里插入图片描述
如果要更换模型,需要先清理缓存(全部清理),然后重新编译。

现在存在两个问题:
1、很多paddlepaddle模型转换会报错(错误就没有具体深究)。
2、速度比较慢(facedetect的demo测试约4-5秒,垃圾分类模型约180ms)。

MNN.js

MNN.js
暂时没有开源

NCNN
环境准备
ncnn+yolov5

6.5fps左右,测试有波动,占用CPU约20几,内存200M左右

tfjs
tfjs+yolov5

7fps左右

openvino

openvino使用emscripten编译,编译cpu的oneDnn报错 oneDNN supports 64-bit architectures only,其余库正常。

tvm

tvm-web
tvm-wasm
注意:将3rdparty下dlpack和dmlc-core下载下来,放到对应的文件夹中。
则可以生成:
dist/wasm/libtvm_runtime.bc
dist/wasm/tvmjs_runtime.wasm
dist/wasm/tvmjs_runtime.wasi.js

onnxruntime
onnx.js

onnxjs
已被onnxruntime web替代

onnxruntime web

language: typescript
dependency: onnxruntime-common, ONNXRuntime WebAssembly
onnxruntime-web工程 2fps左右
tfjs对比工程 7fps左右

onnxruntime node

language: typescript/C++
dependency: onnxruntime-common, ONNXRuntime.dll
onnxruntime web yolov7工程 1.3秒
onnxruntime node yolov7工程 6fps

webAI.js

wasm+wasi替代docker,服务端

webgl

部分功能实现

SQL

SQL

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值