记一次向 WebAssembly 的移植:gl-matrix - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dtysky
V2EX    前端开发

记一次向 WebAssembly 的移植:gl-matrix

  •  1
     
  •   dtysky
    dtysky 2019-06-23 20:19:52 +08:00 3716 次点击
    这是一个创建于 2303 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目地址,欢迎 Star (如果你觉得还行):gl-matrix-wasm

    由于某些工作上的原因,以及个人兴趣,配合之前用 Rust+WASM 写软件渲染器的踩坑,端午花了三天将gl-matrix移植到了 WASM (但因为各种奇奇怪怪的事情一直没时间发文)。此移植包含 gl-matrix 的所有功能,同时具备完整的单元测试。库本身以 Rust + wasm-bindgen + wasm-pack + webpack4 的形式开发,使用 TS + Karma + Mocha 来写单元测试(当然前两年还能扯一扯“优雅”啥的,现在这些都常识了也没啥说的)。在使用方面,我提供了两种使用模式,来应对不同的场合。同时还使用了很多 Trick 来在不破坏工程性的同时优化性能,某种意义上可以当做 Rust 来写 WASM 的工程的模板。

    当然,新技术(或许也不算新了)总是看似很美好,坑却也无限大。具体详细的经历我就不说了(也没空),下面就大概说说一些坑,以及个人认为的 WASM 的优缺点吧。

    坑(缺点)

    坑其实和 WASM 本身关系不是特别大,主要是工具本身的坑,而有的缺点则是 WASM 目前切实的缺点了:

    1. 功能限制。如果你以为有了 Rust 就可以使用 Rust 所有能力快速开发一个 WASM 项目就太天真了,如果你的项目完全使用 Rust 编写或许可以,但一旦要和 JS 端进行交互,那就只能使用 Rust 的一个不大的子集(没有生命周期,没有 trait 等等...),函数无法返回引用(全走裸指针然后unsafe吧 www 要么就全走移动(其实是拷贝))。
    2. 内存管理。没有 GC,需要手动内存管理。其实这确实也不是什么坑,但对于大多数被 JS 惯坏的前端也确实是个问题。而且如果你完全使用 wasm-bindgen 来托管内存管理(虽然还是要在 js 端xx.free()释放),那么你对内存的掌握又会下降,这可能会造成一些性能坑(比如在本库中,由于完全托管给 Rust 创建结构体,比自己在 JS 端直接操作WebAssembly.Memory实例进行ArrayBuffer的操作要慢的多,当然这个还有#4 的问题)。
    3. 灵活性。这个其实是 Rust 的问题,其实仍然不算问题,只是 JS 这边确实有时候有的场景过于灵活,而 Rust 的借用规则由比较严格。举个例子,gl-matrix 中经常会有vec3.add(a, a, b)这种使用方式,而这个是无法通过 Rust 的借用规则的,但它编译到 WASM 后又没办法在编译期检查,咋整呢它搞了个运行时检查,这不但会使得逻辑无法实现还会使得性能有所下降(除此之外还有空指针检查)。当然这正是 Rust 可靠的表现,所以我选择把它黑掉(划掉)。
    4. 工程灵活性。这个主要是 Rust 工具链的可定制性问题。目前这套流程可以生成 wasm + js + ts 头的组合,在够用的情况下很完美。但...如果要做一些魔改(比如修改生成的 JS 来优化性能或自定义功能),就比较难受了,我这边采用的是超级 Hack 的方法,详见wasm-opt.js的黑魔法。
    5. 互操作开销。Rust 本身通过 ABI 和 JS 互调用,但这个开销其实还蛮高的,这个可能会在某些状况打消掉 WASM 本身计算性能的优势。
    6. 异步。不错,目前 WASM 本身编译是强行异步的,这个可能会对代码组织有些坑(主要是库),如果是工程代码使用 Webpack 和异步模块其实也不是啥问题。
    7. 性能。这个其实不是 WASM 的问题,而是 Rust 工具链的问题(应该是),导致同样代码编译出来比 CPP + EMCC 编译出来慢两倍左右,具体见Issue 1585。这个问题我还没查明到底是啥(没空),如果有大佬帮忙再感谢不过。
    8. 体积。不错你没看错,同等功能 WASM 的体积比 JS 大两倍左右(至少在这个纯粹的库)。

    优点

    当然 WASM 本身优点也是有很多的(要不要它干嘛):

    1. 可控。在 WASM 中,内存对于开发者是完全透明的,它就是一块线性的 ArrayBuffer。Rust 在其之上帮我们抽象出了堆和栈,帮我们做了一些事情,但其本质还是非常透明的,而精确的内存控制对于游戏这种复杂应用的好处是巨大的。
    2. 可预测。在 WASM 中,性能是可以预测的。你不用担心 GC ( The world !)或是 JIT 带来的迷失感。同一个函数执行 100 次和 1000 次稳稳差 10 倍左右。
    3. 低开销。GPU 和内存开销都比 JS 更低,这个不用多说。
    4. 性能好。根据不同业务属性,性能提升不太一样。但是纯计算性能毋庸置疑是很高的,当然 JIT 后的 JS 也不差,这个下面会详细说。
    5. 深入融合 Webpack 工作流。这算是一个工程上可用的进步吧(比以前),Webpack4 中 WASM 已经是一等公民了。
    6. 不用再写 JS 啦!对部分开发者这个可能是最重要的(当然有 TS 这个属性可能弱了点)。
    7. 完整。这个是说 Rust 这套流程的,它的web_sysjs_sys实现了 WebIDL 的完整 Port,可用性还是很不错的(如果你不在意性能的话)。
    8. 还有的请补充......

    性能

    性能方面前面也说了,WASM 的运算性能是可预测的,而 JS 则由于 JIT 和 GC 会比较难以预测。对于本应用,由于 JS 版本使用了TypedArrayBuffer,以及进行的都是非常容易优化的简单纯数学运算,所以 JIT 后的 JS 版本性能可以说达到了 JS 可达到的上限。但即使在这种状况下,对于大多数的测试 WASM 版本还是要稳稳压1.5 倍左右:

    详见Benchmark(Matrix4, 2015 RMBP, Chrome)

    而对于真实世界,性能测试就没有 Benchmark 这么简单了,我写了 DEMO 来进行这个测试(当然对于 Web 1000 个物体的场景已经很大了):http://gl-matrix-wasm.dtysky.moe/

    可见其实 WASM 版本在真实场景中还是有优势的(当然,Safari 除外,其 WASM 现在优化得还不行)。

    当然,除了这个库本身我也测试过别的应用,比如 CRC32、数字图像处理、DOM 操作等等,大致结论就是在无法简单优化的密集计算场景下,WASM 有显著优势(比如模型压缩解压缩,加密解密),而在其他场合目前看来就差了点意思,投入产出比较低。

    未来

    未来来看,我觉得 WASM 有几点很值得关注:

    1. WASI。WebAssembly System Interface,不用多说,这说明我们以前对 WASM 本身都有很深的误解,它并不是为 Web 而生的汇编语言,而是个恰好可以跑在 Web 上的通用虚拟机。这是个好东西,对未来 WASM 在更多场景可用而言很重要。
    2. SIMD。simd.js提案被废弃后,SIMD on WebAssembly就成了 Web 使用 SIMD 唯一可预计的方式了,而 SIMD 可以为图形应用带来的好处也不用多说。
    3. 多线程。相比于阉割了SharedArrayBufferWorker(虽然有Transferable对象),这东西在某些场合很重要。

    结论

    一个项目是否要选择 WASM,还是要从其适用性出发。如果你的项目是对计算要求较高,而且没有频繁的 WASM <-> JS 互操作,同时能保证内存都是申请在 WASM 虚拟机中最好,那么你的项目是比较适合使用 WASM 的。否则至少在目前时点不太建议。

    特别要求性能,目前暂时考虑 CPP 版本,Rust 版本在工程化的角度做的很好,而且团队足够重视,应该是未来的主流。

    当然 WASM 本身目前起步也不是很久,它也在不断变好,相信未来它的能力和性能都会更强,也能适用于更多的领域(虽然大多领域应该还是干不过 JS,“我为啥要功夫学这玩意,JS 两下撸完回家打游戏不好吗?”)。

    踩坑或许还不是特别深入,大佬觉得有问题或者建议请指正。

    招人

    本来想发招人的但目前也比较难,不过你有 P7 的实例肯定是没问题的,我们是支付宝互动图形团队,致力于在这个前端迷失的时代,在前端老本行的方向走的更远,给用户带来更好的体验。我们有自研 Web3D/2D 游戏引擎、巨量用户和业务场景,还和小程序团队直接相邻,有想法请直接联系我。

    完。

    3 条回复    2020-03-10 10:52:06 +08:00
    huiyadanli
        1
    huiyadanli  
       2019-06-24 15:45:59 +08:00
    好东西,但是没人看。。。
    dtysky
        2
    dtysky  
    OP
       2019-06-24 18:06:20 +08:00 via Android
    @huiyadanli 因为发布的节点没上首页……
    16fat
        3
    16fat  
       2020-03-10 10:52:06 +08:00
    WebAssembly 不知道什么时候能支持 GC/DOM Integration
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     867 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 19:22 PVG 03:22 LAX 12:22 JFK 15:22
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86