由于js的灵活语法特定, 比如对象内的成员有很多种可能的类型, 导致JIT的优化效果变差.
如下图所示代码, 这里的sum 和a的每一个元素里面每一次计算前后,
从什么类型变成什么类型, 对于编译器来说,
难以一次确定下来, 于是JIT方式就无法在这里起到很好的优化效果.
2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时产生的想法,
专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JS 代码,
但不是普通的 JS,而是一种叫做 asm.js 的 JavaScript 变体
Javascript 去掉动态类型和垃圾回收之后, 就可以像C++一样快速生成机器码.
其实最大的瓶颈是动态类型, 垃圾回收去掉之后, 代码的稳定性会下降
asm.js 只提供两种数据类型, 32位整形, 64位浮点
data | 0表示整数,+data 表示浮点数
asm.js 在浏览器里的运行速度相比JS 提升了几倍.
Asm.js对语法进行了限制, 比如只准使用特定的集中元类型, 整形, 浮点型等.
asm.js对静态类型的问题做的再好,它始终逃不过要解释编译的过程,
而这两步是JavaScript代码在引擎执行过程当中消耗时间最多的两步.
WebAssembly在Asm.js之后. 提出了更完善的方案.
是可以使用更多类型的语言编写代码, 编译生成wasm在浏览器上运行
支持的语言包括C, Rust, Go, Typescript, C#等.
先生成IR 中间码, 中间码是一种很原始的语言叫lisp, 在60年提出, 比C语言的诞生还早13年.
然后从IR再生成.wasm, 其实我们在使用命令编译时, 是没有呈现IR这一个环节的.
当浏览器加载wasm之后, 就会生成特定硬件环境下的, x86/x64或arm机器码.
这里不直接给机器码, 是考虑浏览器是跨平台运行的.
Benchmark测试了一个hash算法, js和wasm在相同计算量的情况下, 性能差别可达几十倍.
Asm.js比js快几倍, Webassembly 又比asm.js快一倍, 或者更多.
wasm速度快, 文件小, 但是是二进制文件, 所以不便于阅读.
所以官方提供了一种wast的文本格式, 方便阅读和调试.
Wast是文本形式, 如果能习惯这种语法, 可以直接编写.
然后通过工具wast2wasm可以编译生成wasm.
Vscode也能找到了插件来以wast的语言形态, 直接浏览wasm文件.
Assemblyscript可以让我们基于typescript语言开发
Webassembly的模块.
由于我们比较熟悉这种语言,
所以我选择从这个角度来进行讲解和演示.
这个环境搭建起来比官方推荐的emscripten方便多了.
编写一个简单的index.ts.
通过命令编译生成wasm和wat
然后就可以在页面上加载执行了.
这是最简单的演示, 真实的情况, 还有内存创建和传入,
传参, 传回调等.
这里语法上, ts 限制使用从i8~64, u8~u64, f32~f64, bool等类型,
其实就跟native语言差不多了, 比如bool只有一个bit, Array, String等等
除此之外, AI, 加密解密的计算也可以通过webassembly来优化的.
浏览器限制直接加载本地dll文件的.
有一种古老的技术 COM, 可以将dll包装成可被其它语言调用的文件,
但是只在windows, IE上兼容, 太局限, 不安全, 难用, 后来随着IE的份额滑落, 渐渐没人用了.
微软基于webassembly技术, 推出Try.net 和Blazor 在网页上的提供C#.net运行的能力.
实现的方法是依托wasm的native运行能力, 远程加载.net的dll文件, 构建运行环境.
有了webassembly, 网页上要下载运行一个操作系统也是没问题的了.
我打开了这个页面, 可一直在下载, 下了几分钟还只有12M, 目标文件有47M,
而且卡死在这个文件上, 后面还有没有文件还不知道, 我就懒得运行它了.