找回密码
 立即注册
查看: 324|回复: 11

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

[复制链接]
发表于 2023-8-15 16:28 | 显示全部楼层 |阅读模式
PuerTS是一个在Unity/Unreal里用Typescript编写逻辑的技术。
之前我为大师介绍过Puer-WebGL方案,其执行性能,开发便捷度,与宿主平台的融合度都远强于Lua。随后我将它搁置到了一边(一直忙于完成PuerTS xil2cpp模式的开发),但也陆续有伴侣开始使用。比来我终于有空,回来进行了一些深入研究,有了一些成果。此次就向大师介绍一下如何更深入地阐扬Puer-WebGL的潜力,使其性能更上一个台阶。
Puer-WebGL的跨语言性能

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



image.png

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

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

  • 跨语言成本可通过业务侧的设计抵消,但执行性能则是硬上限无法改变
即使你用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#(此时耗时大头其实是在衬着上):



image.png

Lua:



image.png

JS:



image.png

合并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:



image.png

JS:



image.png

在JS侧实现Vector操作

优化后,在update函数里还有大量对UnityEngine.Vector3的调用。此时你其实可以在脚本侧实现一个Vector3,后续Vector3运算就直接在脚本侧进行。
这个方式是在很早以前在xLua时期就有的法子。而这时JS生态完善的优势也得以浮现 —— npm上就有一个非常不错的开源数学库 math.gl,它由Uber开发并由OpenJS所赞助的,可靠性较高。
我们将运算改用js的vector后,可以看到效果也有必然提升,已经接近了Lua的效果。而且对vector3的操作越多,这个方式的效果会越明显,



image.png

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

上述法子在每帧的开头和结尾还需要将数值与C#同步,仍会发生JS to C#的通信。有没有法子解决呢?有的。
WASM的内存设计有一点非常不错,浏览器是允许JS通过ArrayBuffer访谒所有wasm内存的。这也就意味着,如果我们在JS侧拿到了一个C#变量的指针,且知道该变量的内存布局,JS就可以直接操作C#对象/布局体的属性。
通过这个法子,JS和C#同步数值时,便不需要通过常规的方式调用Vector API将向量的值设回C#了。直接内存操作即可。
如此优化后最终效果能使JS能运行的盒子数达到了C#的80%(其实此时衬着逻辑所占耗时已是大部门,逻辑运算所占比例,JS和C#都不多)



image.png

到这里为止,优化的代码我已经提交到了微信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



image.png

C#: 50fps



image.png

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

在使用Puer-WebGL时,通过减少Update时的跨语言,将逻辑移至JS,尽量使用TypedArray操作数据三个手段,可以实现全方位超越Lua的效果。
同时,Puer-WebGL也能辅佐Unity WebGL更便操作上多线程API,极限情况甚至做到JS比C#更快。总结数据如下。

以上记录不必然代表最终效果,我写的demo衬着占比还是较高。但我感觉抛砖引玉已经够了
上述优化的ECS部门尚没有考虑代码的封装性,所以后半部门的代码我就先不贴了,而且普洱侧可以沉淀出一些功能来便于你使用。如果你的项目对上述方案有兴趣,欢迎来找我一起探索,你可以在普洱TS的官方QQ群、discord找到我

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-8-15 16:29 | 显示全部楼层
问题是为什么不直接用hybridclr c#[大笑] 除了喜欢用ts我想不出别的理由 别说js的生态
发表于 2023-8-15 16:30 | 显示全部楼层
你是不是连标题都没看就留言了
发表于 2023-8-15 16:30 | 显示全部楼层
[笔芯]
发表于 2023-8-15 16:31 | 显示全部楼层
现实游戏很难这样规整,只更新cube位置
发表于 2023-8-15 16:31 | 显示全部楼层
所有性能测试都是理论值。所以我挺欢迎实际业务来一起探索的。
发表于 2023-8-15 16:32 | 显示全部楼层
合并update的思路挺好的,就是直觉上破坏掉了unity的调用时机
发表于 2023-8-15 16:32 | 显示全部楼层
后面用ECS的时候就没这个问题了
发表于 2023-8-15 16:32 | 显示全部楼层
只有一种技术栈,会越用越窄,吃鸡的成功,让腾讯老板是乐意看见各种技术的碰撞组合
发表于 2023-8-15 16:33 | 显示全部楼层
劣化一下C#性能,就可以让JS更快了[大笑]
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-28 17:25 , Processed in 0.106230 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表