背景:
随着游戏包越来越大,app冷启动的耗时问题也越来越严重,下面将利用一些工具分析一下android启动耗时问题。
主流Unity游戏启动耗时对比
小米4
横屏空包 | 竖屏空包 | 战火与秩序 | 王者荣耀 | 炉石传说 | |
---|---|---|---|---|---|
第一次启动 | 280ms | 327ms | 381ms | 2792ms | 1464ms |
第二次启动 | 287ms | 356ms | 554ms | 3016ms | 1111ms |
第二次启动 | 268ms | 349ms | 368ms | 2959ms | 1098ms |
横屏游戏有个旋转屏幕的过程,比竖屏游戏耗时稍高一点,打两个空包有很明显的对比。
文明与秩序耗时最低,资源包比较小
启动可优化点
对比ab导出到streamingAssets后直接打包,资源包压缩(除第一界面用到的资源全部压缩打包到一起,第一次运行的时候解压缩)后到打包,资源包压缩+bugly子线程初始化打包。
使用下面命令的自动打开界面并返回打开界面的耗时
adb shell am start -W [PackageName]/[PackageName.MainActivity]
小米4
normal | 资源压缩 | 资源压缩+子线程启动bugly | |
---|---|---|---|
第一次启动 | 1716ms | 1012ms | 887ms |
最高 | 1716ms | 1256ms | 1110ms |
最低 | 1576ms | 686ms | 622ms |
三星S4
normal | 资源压缩 | 资源压缩+子线程启动bugly | |
---|---|---|---|
第一次启动 | 2252ms | 1451ms | 1268ms |
最高 | 1451ms | 1268ms | 1735ms |
最低 | 1268ms | 800ms | 777ms |
- 结论:
资源压缩前后对启动耗时影响最大,在启动的时候会感觉到明显的卡顿感。
从启动时间以及log分析通过子线程压缩bugly耗时会缩短100ms左右,卡顿不太明显。
启动完成到第一场景加载可优化点
- 结论:
从上面分析出的GraphicsSettings中引用的shader个数对安装后第一次启动耗时有一定影响。
第一个场景脚本初始化耗时
分析过程
用到的是android的traceView,用法有两种:
第一个是在DDMS界面直接监控你的进程:
第二种是通过在你代码段中埋点,然后输出为.trace文件:
Debug.startMethodTracing("/sdcard/dmtrace.trace");
Debug.stopMethodTracing();
埋点打包运行,然后将对应的.trace文件导出来,拖到DDMS界面就会自动解析出来代码段耗时。
adb pull /sdcard/dmtrace.trace
最后解析出的代码段耗时图如下:
对应一下各个参数。
列名 | 描述 |
---|---|
Name | 该线程运行过程中所调用的函数名 |
Incl Cpu Time | 某函数占用的CPU时间,包含内部调用其它函数的CPU时间 |
Excl Cpu Time | 某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间 |
Incl Real Time | 某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间 |
Excl Real Time | 某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间 |
Call+Recur Calls/Total | 某函数被调用次数以及递归调用占总调用次数的百分比 |
Cpu Time/Call | 某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间 |
Real Time/Call | 同CPU Time/Call类似,只不过统计单位换成了真实时间 |
具体使用可以自行时搜索。
Acticity启动完成前主要是Application初始化以及主Activity初始化耗时
- Application初始化
从traceView导出的耗时图可以看到Bugly耗时比较严重,占了Allication初始化耗时的80%以上。
小米4 | 三星s4 | |
---|---|---|
第一次启动 | 180.7ms 1024.5ms | 196.3ms 529.1ms |
第二次 | 221.8ms 1423.8ms | 211.5ms 463.9ms |
第三次 | 262ms 482.2ms | 197ms 377.9ms |
第四次 | 246.2ms 439.4ms | 193.8ms 491ms |
Bugly初始化耗时(Incl Cpu Time+Excl Real Time)
小米4 | 三星s4 | |
---|---|---|
第一次启动 | 180.7ms 1024.5ms | 196.3ms 529.1ms |
第二次 | 221.8ms 1423.8ms | 211.5ms 463.9ms |
第三次 | 262ms 482.2ms | 197ms 377.9ms |
第四次 | 246.2ms 439.4ms | 193.8ms 491ms |
Application初始化中,Bugly耗时比较严重,可以放在子线程中进行初始化。调用cpu耗时和真实耗时相差比较大,可能与traceView输出调试数据io有关系,实际测试的时候bugly耗时更接近cpu time
根据UnityPlayer初始化看各个地方卡顿点:
- UnityPlayer 初始化 nativeFile和Native.Load的影响
UnityPlayer是在UnityActivity onCreate的时候进行的初始化,该段的耗时主要是nativeFile简单的看了一下源码,这个方法会为文件创建一个索引目录,包括了文件的名称、大小、校验码等等,所以猜测这里的卡点主要是与文件的数量有关。这里我把StreamingAssets目录下放不同数量以及大小的文件进行测试NativeLoader.load+nativeFile的耗时。
Q | 小米4 | 三星S4 |
---|---|---|
7420个文件 压缩后256M 第一次启动 | 116.4ms+ 237.5ms(417.3ms) | 159.2ms+1774.5ms(2016.8ms) |
7420个文件 压缩后256M 第二次启动 | 109.3ms+190.9ms(452.3ms) | 65.3ms+646.3ms(783.1ms) |
一个压缩包 压缩后217M 第一次启动 | 42.4ms+32.2ms(184.5ms) | 130.5ms+70.1ms(273.5ms) |
一个压缩包 压缩后217M 第二次启动 | 34.7ms+25.3ms(134.8ms) | 66.5ms+20.1ms(142.3ms) |
没有文件 第一次启动 | 40.5+30.2ms(207.5ms) | 45.3ms+20.2ms(147.4ms) |
没有文件 第二次启动 | 33.8+23.6ms(157ms) | 39ms+31.9ms(129.5ms) |
这里统计的都是Incl Real Time(NativeLoader.load+nativeFile(UnityPlayer.init(unityPlayer初始化耗时)))
Q | 小米4 | 三星S4 |
---|---|---|
7420个文件 压缩后256M 第一次启动 | 116.4ms+ 237.5ms(417.3ms) | 159.2ms+1774.5ms(2016.8ms) |
7420个文件 压缩后256M 第二次启动 | 109.3ms+190.9ms(452.3ms) | 65.3ms+646.3ms(783.1ms) |
一个压缩包 压缩后217M 第一次启动 | 42.4ms+32.2ms(184.5ms) | 130.5ms+70.1ms(273.5ms) |
一个压缩包 压缩后217M 第二次启动 | 34.7ms+25.3ms(134.8ms) | 66.5ms+20.1ms(142.3ms) |
没有文件 第一次启动 | 40.5+30.2ms(207.5ms) | 45.3ms+20.2ms(147.4ms) |
没有文件 第二次启动 | 33.8+23.6ms(157ms) | 39ms+31.9ms(129.5ms) |
结果表明文件的多少对UnityPlayer初始化有一定的影响,而UnityPlayer初始化是放在主Activty onCreate中的,比较英雄初始化耗时,源码里面是nativeFile对所有的文件都遍历一遍做了索引。
游戏中资源采用了压缩包的形式,降低NativeLoader.load+nativeFile的耗时。
到这里Activity初始化完成,开始进行游戏初始化操作
- UnityInitApplication阶段耗时问题
在developmentbuild模式下各个阶段都会有log
-
初始化mono/il2cpp
-
InitializeEngineNoGraphics
-
InitializeGfxDevice
- PlayerInitEngineGraphics
初始化完成后就会进行nativeRender游戏循环阶段,开始初始化dll(mono打包),初始化cpp(il2cpp),初始化资源等等。这个阶段只有根据调试日志查看,在PlayerInitEngineGraphics阶段耗时情况比较严重。
- GraphicsSettings的数量对第一次启动的影响
PlayerInitEngineGraphics阶段耗时
三星 | 小米4 | |
---|---|---|
87个shader | 37s 40s | 4.8s |
一半的shader | 27s 28s | |
9个shader | 16s | |
2个shader | 5s | 1.6s |
默认的7个shader | 14s 13s | 1.35s |
没有shader | 9s 6s | 1.2s |
这里看出GraphicsSettings中shader的数量对第一次启动的时间还是有一定的影响,对三星S4上比较严重。
小米4上shader影响的截图:
-
4个第一场景必要的shader
-
87个shader
87个shader比3个shader第一次启动慢了3s左右。
- UnityLoadApplication
初始化完成之后显示splash然后开始加载第一个场景,耗时主要是第一个场景脚本初始化的耗时。
Resource下文件的数量对启动速度的影响:(到Unity第一个场景显示完成的耗时)
1330个文件9.67M(项目中的lua文件放在Resource目录下)
Q | 小米4 | 三星S4 |
---|---|---|
第一次启动 | 6.1s | 16.4s |
第二次启动 | 5.5s | 2.8s |
Resource为空 第一次启动 | 6.2s | 18.4s |
Resource为空 第二次启动 | 5.5s | 2.76s |
这里的结果表示项目中Resource目录下的文件对启动耗时影响不大。(手机当前cpu、内存状态等对启动时间也有很多影响,所以这里测出来resource空的运行时间还稍长)
现在Unity官方不再推荐大家使用Resources文件夹,我们未来可能把这个功能关闭掉也是有可能的,因为你把资源放在这里面的话,它是有几个缺点的,第一个缺点你游戏启动的时候,比如说我们手机上面启动这个游戏,它第一步会把Resources文件夹内的文件构建一个索引,这样你后面再动态加载资源的时候,他可以在这里面查找这个文件,这样做导致游戏启动比较耗时,会发现启动黑屏的时间很长,这样对玩家来说这样的体验不太好。
另外,构建索引会占用你更多系统的内存资源。所以说我们建议以后大家不要使用Resources文件夹。
使用小米4粗略测试一下resource包中放大量文件(资源文件(34254个文件951.6M)复制到resource目录下生成apk128M)和StreamingAssets放入大量文件对Activity启动的影响
- resource包启动
- StreamingAssets启动(7420个文件250.1M)
- 空的工程启动
粗略测试了一下,Resource目录下放大量文件对UnityPlayer初始化有一定影响。