likes
comments
collection
share

【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

作者站长头像
站长
· 阅读数 15

PuerTS是一个在Unity/Unreal里用Typescript编写逻辑的技术。

Puer-WebGL的跨语言性能

上一篇文章中我着重介绍了Puer-WebGL方案下,JS执行性能相对于Lua的压倒性优势。但我也在官方demo中附上了一个性能测试,其中可以看到Puer-WebGL的跨语言性能,是比Lua更弱的。

【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

出现这个原因很正常:Puer-WebGL下JS是运行在宿主JS引擎(浏览器OR小游戏)的,它需要进行JS<->WASM通信来完成对C#的调用。但Lua则是虚拟机本就跑在WASM里面,跨语言链路相对没那么长(具体来说就涉及到wasm通信的设计了,本文暂不表),因而,直接跨语言的话,Lua在WebGL上表现并不比JS差

但是,这并不重要,原因有两点:

  1. 实际游戏里,跨语言的消耗占比没有你想象的高

很多人喜欢在调研的时候将跨语言性能执行性能两者放在同等的重要程度上考量,但根据Puer-WebGL的实际使用者反馈,跨语言消耗占比远没有执行消耗的占比高。所以一般来说,整体效果JS还是比Lua更优。

  1. 跨语言成本可通过业务侧的设计抵消,但执行性能则是硬上限无法改变

即使你用Lua(以及任意其他脚本语言),跨语言操作也是应该尽量减少的。这个优化操作并不难,本文接下来的内容就会介绍。而WebGL环境下,JS优化后的上限显然比Lua更高。试想,你在开发业务时,若通过业务侧的优化,JS最终的性能上限能达到C#的量级,这和Lua被禁锢在虚拟机性能上限是完全不一样的 —— 我命由我不由天,谁不喜欢?

跨语言性能优化实录

以下就记录一下我对一个demo进行性能优化的实录,使你对这整个流程有一个了解。

写个demo看看原始状态

我用三种语言写了个简单的demo,并各自在WebGL下运行 —— 不停的生成一些方块,它们每帧都会往中心移动(Vector3计算)。JS与Lua都采用最简单的跨语言调用方式:将Vector操作的脚本函数赋值给MonoBehaviour的Delegate,并在MonoBehaviour的OnUpdate里调用该delegate(产生跨语言)。

测试结果是C#能支持5000个盒子跑50fps,Lua支持1000个盒子跑40fps,而JS在400个时就已经只剩20+fps了。和前面的跨语言测试基本一致。

C#(此时耗时大头其实是在渲染上): 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

Lua: 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

JS: 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

合并update

在跨语言优化中第一个可以做的是合并update。在PuerTS的官方demo上就有讲到一点:每帧对每个Component都进行一次C# to JS的update调用,本身的消耗是很高的。

我们可以在JS侧实现一个Update调度器。在js对象创建时,就往这个调度器添加update函数的回调。随后每帧只在调度器处进行一次C# to JS update,剩下的都由JS派发即可。

经过这样的优化,Lua在WebGL下没有太大变化,但JS从原本的400盒子20+,可以变为1000盒子20+

Lua: 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

JS: 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

在JS侧实现Vector操作

优化后,在update函数里还有大量对UnityEngine.Vector3的调用。此时你其实可以在脚本侧实现一个Vector3,后续Vector3运算就直接在脚本侧进行。

这个方法是在很早以前在xLua时期就有的办法。而这时JS生态完善的优势也得以体现 —— npm上就有一个非常不错的开源数学库 math.gl,它由Uber开发并由OpenJS所赞助的,可靠性较高。

我们将运算改用js的vector后,可以看到效果也有一定提升,已经接近了Lua的效果。并且对vector3的操作越多,这个方法的效果会越明显,

【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

通过buffer操作实现C#类赋值

上述办法在每帧的开头和结尾还需要将数值与C#同步,仍会产生JS to C#的通信。有没有办法解决呢?有的。

WASM的内存设计有一点非常不错,浏览器是允许JS通过ArrayBuffer访问所有wasm内存的。这也就意味着,如果我们在JS侧拿到了一个C#变量的指针,且知道该变量的内存结构,JS就可以直接操作C#对象/结构体的属性。

通过这个办法,JS和C#同步数值时,便不需要通过常规的方法调用Vector API将向量的值设回C#了。直接内存操作即可。

如此优化后最终效果能使JS能运行的盒子数达到了C#的80%(其实此时渲染逻辑所占耗时已是大部分,逻辑运算所占比例,JS和C#都不多)

【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录 到这里为止,优化的代码我已经提交到了微信Unity小游戏转换插件仓库上作为示例

天作之合? - Puer-WebGL + Unity ECS

跟PuerTS的一位重要贡献者交流过后。他提醒我上述优化和Unity ECS模式有了许多异曲同工的地方,我深以为然。思路上,上述优化手段和ECS有几处一样:都在一个地方统一调度所有实体的Update逻辑,都尽可能让游戏实体只存储数据不包含逻辑(这个是重点,它提供了跨线程友好度的同时,某种程度也提供了跨语言的友好度)。

因此,我又写了一个Unity ECS实现上述功能的demo,分别测试了纯C#以及使用Puer实现逻辑的效果。

这个情况下JS的效率跟C#更为接近了。并且由于思路更为接近,这种写法能更容易避免不得不产生的C#调用。

但事情还没结束。

执行性能再优化 —— 让JS比C#更快

做到上一步后,笔者就在想,有没有可能再优化一下让JS比C#更快呢?

听起来会有些不可思议。哪怕C#被转换成了WASM,但是要说JS能比C#快,恐怕有些反直觉。是的,同样的环境下JS不可能比WASM里的C#更快。

但脑子转个弯的话,是存在突破口的 —— 上面说到Unity ECS设计很易于编写多线程逻辑,但Unity WebGL的WASM,目前还只能单线程执行

在Unity的文档中,有一个设置可以开启多线程的支持,但根据Unity论坛里官方人员今年的描述,该多线程支持依赖于WASM Thread,该浏览器功能目前还很早期,据说对性能的优化还很有限(笔者尚未深究)。

而且浏览器标准覆盖是需要时间的,比如说WeakRef诞生至今五年,但国内iPhone仍未到达99%覆盖率。

而JS目前已经可以自如地使用Worker Thread和SharedArrayBuffer实现多线程了,浏览器已经早早地支持了它们,同时还配套加上了Atomic原子操作等辅助类。

为了测试 JS+多线程 vs WASM+单线程 的性能优劣,笔者为之前的demo加上了一个O(n^2)的碰撞检测,以增加逻辑部分相较于渲染的比重。得益于ECS的设计,这个改动并不难。

最终在浏览器里,能录得JS比WASM更快。(其实渲染的比重还是太高,否则能有更大的差距。而且编写过程中笔者觉得JS+Worker还有几个优化点可做)

JS+Worker:65fps

【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

C#: 50fps 【Unity WebGL】让JS比C#还快 —— Puer-WebGL 性能优化实录

当然,JS线程跑满,对移动设备的耗电量也许是个挑战(笔者没测试),是否真的采用JS写逻辑可以根据业务实际情况,实际测试后再评估。但至少可证,借助Puer,能让你用Unity写WebGL游戏时更方便地用上线程能力。

总结

在使用Puer-WebGL时,通过减少Update时的跨语言,将逻辑移至JS,尽量使用TypedArray操作数据三个手段,可以实现全方位超越Lua的效果。

同时,Puer-WebGL也能帮助Unity WebGL更方便用上多线程API,极限情况甚至做到JS比C#更快。总结数据如下。

模式盒子个数fps简单打分(盒子数xFPS,逻辑加重再x2)
Lua10004040k
Lua (合并Update)10004040k
JS4002510k
JS (合并Update)10002525k
JS (合并Update+JSVector)10004040k
JS (合并Update+JSVector+内存操作)400050200k
C#(OOP)500050250k
逻辑加重后
C# (ECS)300050300k
JS (ECS+worker+上述所有优化)300065390k

以上记录不一定代表最终效果,我写的demo渲染占比还是较高。但我觉得抛砖引玉已经够了

上述优化的ECS部分尚没有考虑代码的封装性,所以后半部分的代码我就先不贴了,而且普洱侧可以沉淀出一些功能来便于你使用。如果你的项目对上述方案有兴趣,欢迎来找我一起探索,你可以在普洱TS的官方QQ群、discord找到我

转载自:https://juejin.cn/post/7260681778468716605
评论
请登录