20

你想要的WebAssembly入门与实践

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI2NTk2NzUxNg%3D%3D&%3Bmid=2247486074&%3Bidx=1&%3Bsn=dc104a309228f872864b9fe566d95d8a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
AneuAnz.png!webiqQrIr7.png!web

写在开头

  • 不为了追寻潮流而学习某个技术,本人仅做最基础的入门与实践讲解

  • 欢迎收藏前端生活社区: https://qianduan.life
  • 想要加入资源群和前端交流群可以看文末

WebAssembly是什么,可以吃吗?

官网介绍:

  • WebAssembly是由主流浏览器厂商组成的 W3C 社区团体 制定的一个新的规范

  • WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式

webAssembly的特点

高效

  • WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率

安全

  • WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly将会严格遵守同源策略以及浏览器安全策略。

开放

  • WebAssembly 设计了一个非常规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。可以以这种文本格式在web页面上查看wasm模块的源码。

标准

  • WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非web环境下。

正式开始(要凑字数,理解)

  • Node.js有C++插件,Addon模块,还能调用C#插件,go插件,还能跟他们通信

  • 我show一段我之前写的代码吧.用子进程调起c#插件,然后通信.(其实这个是我写的当时windows平台的截图插入到自研的文本编辑器中,桌面软件)

// Node.js主进程中调起子进程
 await screen_window();
 
//function screen_window
import { execFile } from 'child_process';
import path from 'path';
import ipcSend from '../main/utils/ipcSender';
function screen_window() {
  return new Promise((resolve, reject) => {
    const screen_window = execFile(path.resolve($dirname, '../screenshot/PrintScr.exe'));
    screen_window.on('exit', function(code) {
      if (code === 1) {
        ipcSend.insertImage();
      }
      resolve();
    });
  });
}

export default screen_window;

既然Node.js有拓展能力,那么浏览器环境呢?必然也需要

  • 当时为了处理一个speex格式的音频在H5中实现动态播放,我封装了一个7000行的库,里面大量的8进制...泪

  • 不过最后帮助到了很多人,希望对你有用 https://github.com/JinJieTan/speex-in-h5
  • 处理音视频的时候,多考虑下各种插件,webAssembly...

什么情况需要考虑到使用 webAssembly ?

  • 首先给大家一个链接, https://www.wasm.com.cn/demo/Tanks/ ,这是 坦克!,Unity 教程 中的一个游戏 导出成WebAssembly 的游戏.
  • 很流程,丝滑般顺畅

怎么理解webAssembly这个技术?

  • 写到这里,还是很多人不理解,到底什么是webAssembly啊!

  • Node.js可以直接运行C# C++代码吗?当然不可以,只能调用操作系统能力,或者+中间层或者其他方式调用。

  • 浏览器可以运行 C++代码,rust吗?当然也不可以(如果可以的话,你告诉我,我把这里改了)

  • 那么,webAssembly模块必然是要被编译成浏览器可以识别的语言,然后被JS调用,可以看成C++ ADDON一样的形式吧,我个人理解

在这里,我要强调一件事

  • Electron中,虽然分主进程和渲染进程,但是主进程阻塞,同样会阻塞渲染进程,GC也是会阻塞的。回收300MB,需要1S还是多久,忘记了,去年做过实验

  • 那么浏览器中,这种调用webAssembly模块的情况下,理论上如果webAssembly模块被阻塞了,那么JS主解析线程也是会阻塞的。 可是我在国外网站上看到的内容是说:每个WebAssembly线程都在Web Worker中运行,相当于跟JS主解析线程是分开的,不会阻塞JS主线程的解析
  • 最近看到又新引入的 SharedArrayBuffer 和原子操作使开发人员能跨多个线程使用共享的内存了。这样以来就能实现更细粒度的并发算法,避免过于硬核引发不适,可以自行跳转 https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management/

如何编写webAssembly模块

  • 将下面这段代码复制到浏览器控制台 就可以运行了

WebAssembly.compile(new Uint8Array(`
  00 61 73 6d  01 00 00 00  01 0c 02 60  02 7f 7f 01
  7f 60 01 7f  01 7f 03 03  02 00 01 07  10 02 03 61
  64 64 00 00  06 73 71 75  61 72 65 00  01 0a 13 02
  08 00 20 00  20 01 6a 0f  0b 08 00 20  00 20 00 6c
  0f 0b`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(module => {
  const instance = new WebAssembly.Instance(module)
  const { add, square } = instance.exports

  console.log('2 + 4 =', add(2, 4))
  console.log('3^2 =', square(3))
  console.log('(2 + 5)^2 =', square(add(2 + 5)))

})
  • 输出结果:

jyqURfz.png!web

这里应该大家能看出来,webAssembly模块,其实就是二进制文件

  • 你编写的webAssembly模块,无论是什么语言,他最终应该是一段二进制文件,然后被前端通过ajax获取

  • 如何编译: https://www.wasm.com.cn/getting-started/developers-guide/

如何加载/运行webAssembly模块?

  • 在未来计划中,WebAssembly 模块可以使用 ES6 模块(使用 <script type="module"> )加载,WebAssembly 目前只能通过 JavaScript 来加载和编译。基础的加载,只需要3步:
    • 获取 .wasm 二进制文件,将它转换成类型数组或者 ArrayBuffer

    • 将二进制数据编译成一个 WebAssembly.Module

    • 使用 imports 实例化这个 WebAssembly.Module,获取 exports。

使用webAssembly模块示例:

  • 首先定义加载webAssembly的功能函数:

/**
 * @param {String} path wasm 文件路径
 * @param {Object} imports 传递到 wasm 代码中的变量
 */
function loadWebAssembly (path, imports = {}) {
  return fetch(path)
    .then(response => response.arrayBuffer())
    .then(buffer => WebAssembly.compile(buffer))
    .then(module => {
      imports.env = imports.env || {}

      // 开辟内存空间
      imports.env.memoryBase = imports.env.memoryBase || 0
      if (!imports.env.memory) {
        imports.env.memory = new WebAssembly.Memory({ initial: 256 })
      }

      // 创建变量映射表
      imports.env.tableBase = imports.env.tableBase || 0
      if (!imports.env.table) {
        // 在 MVP 版本中 element 只能是 "anyfunc"
        imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
      }

      // 创建 WebAssembly 实例
      return new WebAssembly.Instance(module, imports)
    })
}
  • 然后外部调用,传入存放这个WebAssembly模块的资源接口

loadWebAssembly('path/to/math.wasm')
  .then(instance => {
    const { add, square } = instance.exports
    // ...
  })
  • 只要通过请求获取到了它,然后处理后,就可以获取到exports出来的内容了

想要更深入了解的,可以参考下webAssembly和ffmpeg实现前端转码

  • 导出一个入口函数到上层js

  • 传入函数参数来控制ffmpeg命令行参数

  • 通过虚拟文件系统传入输入文件以及获取输出文件

  • https://zhuanlan.zhihu.com/p/27910351 使用方法:
self.importScripts('ffmpeg.js');
onmessage = function(e) {
  console.log('ffmpeg_run', ffmpeg_run);
  var files = e.data;
  console.log(files);
  ffmpeg_run({
    arguments: ['-i', '/input/' + files[0].name, '-b:v', '64k', '-bufsize', '64k', '-vf', 'showinfo', '-strict', '-2', 'out.mp4'],
    files: files,
  }, function(results) {
    console.log('result',results);
    self.postMessage(results[0].data, [results[0].data]);
  });
}
  • 涉及到转码,压缩这些事情的时候,就要多考虑webAssembly了,当然node.js本身命令传参调用ffmpeg也可以,像图片压缩这种事情,Node.js也做不好的,就算上了C++插件也不行,CPU吃得很,并发稍微上去点,CPU就打到百分百.

最后

欢迎加我微信( CALASFxiaotan ),拉你进技术群,长期交流学习...

欢迎关注「 前端巅峰 」,认真学前端,做个有专业的技术人...

点个在看支持我吧,转发就更好了

我在看


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK