问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

性能优化-把 Scratch 的加载用时降低 60%

创作时间:
作者:
@小白创作中心

性能优化-把 Scratch 的加载用时降低 60%

引用
1
来源
1.
https://juejin.cn/post/7444452782867808295

本文将介绍如何通过优化Scratch工程文件的加载流程,将加载时间降低60%。文章详细分析了Scratch工程文件的加载过程,并提出了具体的优化方案,包括设计流式文件格式、避免JS解压、优化MD5计算和SVG处理等。

最近几个月在公司里做Scratch的研发工作,主要负责性能优化这一块.
如果你没听过Scratch, 它是麻省理工开发的的一款图形化编程的 web 应用, 你可以在网页上通过拼接代码块来开发一些游戏应用
Scratch 也被认为是一种编程语言, 很多做少儿编程教育的公司会基于 Scratch 进行教学
这里有一个用 Scratch 实现的应用,建议先玩一下
我司的安卓app是基于
chromium
来加载
web
应用的
在一些低算力的平板上(点名小度学习机😂),
scratch
工程甚至会加载一分钟的时间, 非常影响用户体验. 在经过一系列设计优化后, 我把线上的
scratch
工程的平均加载耗时降低了60%, 这里跟大家分享下一些思路和心得

Scratch 是如何加载工程文件的

下载 sb3 文件

Scratch 首先会去下载一个后缀为
.sb3
的文件,里面包含了一个
scratch
工程的所有信息
虽然后缀是
.sb3
, 但是这个文件实际上就是一个
zip
文件, 你可以把后缀改成
.zip
, 就可以正常解压这个文件. 解压过后, 里面是一堆图片和音频文件还有一个名为
project.json
的文件,如下图所示

加载角色对应的资源

当 Scratch 把这个文件下载完之后, 它会根据
project.json
的文件来加载所有的资源,
project.json
里面是各个角色和他们对应的图片和音频的
md5

可以看到
costumes
就是它所有的的图片,
sounds
就是它所有的音频
scratch
通过循环替换这个角色的图片,播放它的声音, 就可以做出来角色唱唱跳跳的游戏应用
除此之外,
Scratch
还会对这些资源文件进行md5后再存入
scratch-storage
对于
svg图片

Scratch
会对它的内容进行处理和修正,为了保证后续能正确加载并渲染
svg 图片
通过
Profile
,可以非常直观的看到
Scratch
加载的具体耗时都发生在哪里

Scratch
加载的时候,大量出现了上面的函数调用,这个函数调用来源于
jszip
,也就是解压
.sb3
(zip) 文件时调用的
第二个耗时的函数调用是
md5
, 每个资源在存入
scratch-storage
的时候都产生了
md5计算

总结并梳理一下上面的信息


下面是
Scratch
加载工程文件中比较耗时的步骤
2. 下载
.sb3
文件
4. 用
jszip
解压
.sb3
文件
6. 对每个资源做
md5
8. 解析并重新生成
svg
的数据,
scratch
专门为这个逻辑写了个库scratch-svg-renderer
10.
scratch-render
使用
webgl
对图片进行二维纹理图像生成 (textureImage2d),用于后面渲染图片
上面的哪些步骤可以优化呢?读者可以简单思考一下这个问题

如何优化

流式的文件格式

对于问题1,我们似乎没有什么处理手段,下载就一定会有下载时间的。
先看问题2,如何避免
unzip
解压的过程。
http 协议
本身就支持
gzip
等压缩算法传输数据,没有必要在
js
里面进行解压过程。 浏览器原生来做解压肯定比在
js
里面做解压要快的多
这里的思路是,设计一种新的文件格式编码所有的文件,本身不做压缩处理,在
http
传输的过程中通过
gzip
传输。保证体积不变的同时,利用浏览器原生代码解压,这样更快。
我借鉴了
flv
的编码格式,设计了一套新的编码格式
文件会由若干个
chunk
组成, 每个
chunk
都由一个
header
和一个
data
组成
header
里面描述后面跟随的
data
的大小和类型,就像一个单链表一样。
data
就是
svg
|
png
|
mp3
|
wav
等文件数据
这样做有2个好处
2. 不再是
zip
格式的文件,没有在
js
里面解压的负担, 只需要在网络传输的时候
gzip
就可以了
4. 文件是流式的,可以一边下载一边解析
现在浏览器支持
stream
的形式处理http response

  
fetch().then(resp=>resp.body.getReader())
  

也就是说,没必要等完全下载完了再开始解析的过程,只要第一个
chunk
下载完了,就可以先拿到一个
chunk
做后续加载工作

回到问题1,虽然没有办法减少下载的时长,但是如果设计了这套格式,就可以一边下载一边做后续解析的工作了 , 并行化下载和加载
伪代码如下所示

  
const reader = await getResponseStreamReader()
while (!reader.done){
  // 读 header
  const header = await reader.read(headerByteLength)
  // 读文件数据
  const fileData = await reader.read(header.dataByteLength)
}
  

对于问题3,所有的
chunk data
都计算好
md5
放在
header
里面
对于问题4,所有的
svg data
都先通过
scratch-svg-renderer
处理过再放在文件里面
这样只要写好一个编码器,还有一个解码器就可以了

总结一下

对于每个问题和对应的解决方案
问题 解决方案
下载.sb3文件的耗时 用流的形式处理文件,并行化下载和加载,充分利用下载的时间
jszip解压.sb3文件 放弃zip的文件格式, 在 http 传输的时候 gzip 即可, 这样js代码只要读 ArrayBuffer 就行了
md5 算好md5放在header里面, 加载的时候直接读出来用
解析svg 所有的svg data都先通过scratch-svg-renderer处理过再放在文件里面
对图片进行二维纹理图像生成 (textureImage2d) 这一步似乎没有办法??

一个比较形象的流程图如下所示
理想状态下,下载结束的同时加载也并行完成

结果

最后统计了一下上线后的数据,平均加载耗时降低了 60%,完美!
但是还有一个问题
scratch
利用
webgl
对图片处理的步骤能不能也略过?其实也是可以的,但是稍微有点复杂,可能需要另外一片文章才能解释清楚
欢迎大家在评论区友好交流,谈谈自己的想法😁

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号