微信小游戏背后的技术优化,可以让所有小游戏获得更好性能( 二 )


mark
针对 js 的 WeixinJSCore.invokeHandler 接口提供专门的 C++ 裸绑定接口 InvokeHandler,取出所有参数后,只需要一次 JNI 调用到 nativeInvokeHandler,然后调到具体 API 的 Java 实现函数 Invoke 。
除此之外,针对异步 JsApi 调用,之前的流程是在 java 层抛到另一个线程执行 。有了 libuv Looper 后,优化成在底层起一个 uv 的 worker 线程,通过 ASyncCall 将任务抛到 worker 线程,这样 worker 里只需要执行同步的 api 流程,流程上简化了,效率上比抛 Java 层线程更高 。
2. JsApi 回调优化当框架层需要触发 JS 回调时,之前的做法是拼好一段 JS 字符串然后 evaluate:
evaluateJavascript(String.format(  "typeof WeixinJSBridge !== 'undefined' && " +  "WeixinJSBridge.invokeCallbackHandler(%d, %s)",  callbackId, data));这里的本质是去调用 JS 里的统一回调处理函数 WeixinJSBridge.invokeCallbackHandler,采取了直接执行一段 JS 的方法 。优点是实现简单,缺点是效率不高 。
因为让 JS 引擎执行一段 JS 代码时,需要先编译,parse 抽象语法树,生成 Ignition 字节码,甚至启用到 TurboFan 编译优化器,最后才真正执行到想调用的 JS 函数 。
同时每个回调都拼一个字符串执行,在 JS 引擎内部会积攒大量临时字符串,占用内存资源 。
优化的方法其实也很简单,就是通过 jsbinding 预先查找好 WeixinJSBridge.invokeCallbackHandler 函数,在需要回调这个 JS 函数时,直接调用即可 。
// 查找到 invokeCallbackHandler 函数后,保存下来mm::JSObject func =         JS_GET_AS(mm::JSObject, js_bridge, "invokeCallbackHandler");js_func_holder_ = JS_NEW_OBJECT_HOLDER(func);// ...// 当需要回调时,直接调用JS_CALL(js_func_holder_->Get(), nullptr, nullptr,              js_bridge, callbackId, data);并行调用优化开发者在执行某些耗时较重的任务时,可以使用多线程 Worker,类比标准 H5 的 WebWorker 。
// 主线程初始化 Workerconst worker = wx.createWorker('workers/request/index.js') // 文件名指定 worker 的入口文件路径,绝对路径// 向 Worker 发送消息worker.postMessage({  msg: 'hello worker'})// workers/request/index.js// 在 Worker 线程执行上下文会全局暴露一个 `worker` 对象worker.onMessage(function (res) {  console.log(res)})之前的 Worker 有个限制,只能执行一些纯逻辑运算的代码,不支持 JsApi 的调用 。这很大程度限制了 Worker 的使用,于是我们也在不断的扩展 Worker 的能力,增加了音频、网络、文件等能力 。
// Worker 线程var audio = worker.createInnerAudioContext()audio.src = urlaudio.play()未来 Worker 将会赋予更多能力,提高开发者并行化处理的效率 。
数据传输优化开发者在 JS 层的数据(ArrayBuffer)需要传到客户端底层,同时客户端底层的数据也需要传到 JS 上层,这中间涉及到数据的高效传输 。在渲染优化时,可以通过 wgfx 提供的 createNativeBuffer 接口,创建一块 JS 和 Naitve 共享的内存,双方可直接读写该内存而无需额外的传输,极大的提高了效率 。
NativeBuffer 的共享内存传输机制,可以应用到多个需要频繁传输数据的场景,比如 Camera 传输的数据、JS 的 WebGL CommandBuffer 传输等等 。
还有一种情况是前面提到的 Worker 之间传输数据,如果通过默认的 postMessage 来传输,效率是非常低的,不利于传输较大的 ArrayBuffer 数据 。为了解决这个问题,我们提供了类似标准 H5 的 SharedArrayBuffer 的能力,用来 Worker 之间高效的传输数据 。
// game.jsconst sab = wx.createSharedArrayBuffer(2)worker.postMessage({  sab})// worker.jsworker.onMessage(function (res) {  res.sab.lock(() => {    setTimeout(() => {      res.sab.unlock()    }, 3000)  })})总结【微信小游戏背后的技术优化,可以让所有小游戏获得更好性能】小游戏的性能瓶颈,很大程度局限于 JavaScript,而我们所做的各种优化,是希望能尽量抹平 JavaScript 本身带来的性能损耗,接近并向原生性能靠齐,极具困难和挑战 。
在 IOS 上,我们也为让 JavaScript 拥有 JIT 能力做了深入探索 。同时,我们也在 WebAssembly 上也进行了深入的探索和支持,未来有机会再进行分享 。


推荐阅读