Unity启动耗时优化

背景:

随着游戏包越来越大,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界面直接监控你的进程:


Unity启动耗时优化_第1张图片
ddms

第二种是通过在你代码段中埋点,然后输出为.trace文件:

Debug.startMethodTracing("/sdcard/dmtrace.trace");
Debug.stopMethodTracing();

埋点打包运行,然后将对应的.trace文件导出来,拖到DDMS界面就会自动解析出来代码段耗时。

adb pull /sdcard/dmtrace.trace

最后解析出的代码段耗时图如下:

Unity启动耗时优化_第2张图片
耗时

对应一下各个参数。

列名 描述
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初始化
Unity启动耗时优化_第3张图片
application启动耗时.png

从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初始化看各个地方卡顿点:

Unity启动耗时优化_第4张图片
image
  • UnityPlayer 初始化 nativeFile和Native.Load的影响

UnityPlayer是在UnityActivity onCreate的时候进行的初始化,该段的耗时主要是nativeFile简单的看了一下源码,这个方法会为文件创建一个索引目录,包括了文件的名称、大小、校验码等等,所以猜测这里的卡点主要是与文件的数量有关。这里我把StreamingAssets目录下放不同数量以及大小的文件进行测试NativeLoader.load+nativeFile的耗时。


Unity启动耗时优化_第5张图片
nativefile.png
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阶段耗时问题
Unity启动耗时优化_第6张图片
UnityInitApplication流程

在developmentbuild模式下各个阶段都会有log

  1. 初始化mono/il2cpp


    Unity启动耗时优化_第7张图片
    初始化monoil2cpp耗时.png
  2. InitializeEngineNoGraphics


    Unity启动耗时优化_第8张图片
    InitializeEngineNoGraphics耗时.png
  3. InitializeGfxDevice


    Unity启动耗时优化_第9张图片
    InitializeGfxDevice耗时.png
  4. PlayerInitEngineGraphics
Unity启动耗时优化_第10张图片
PlayerInitEngineGraphics耗时.png

初始化完成后就会进行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影响的截图:

  1. 4个第一场景必要的shader


    4shader.png
  2. 87个shader


    Unity启动耗时优化_第11张图片
    87个shader.png

    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包启动
Unity启动耗时优化_第12张图片
resource包启动
  • StreamingAssets启动(7420个文件250.1M)
Unity启动耗时优化_第13张图片
StreamingAssets启动
  • 空的工程启动
Unity启动耗时优化_第14张图片
空的工程启动.png

粗略测试了一下,Resource目录下放大量文件对UnityPlayer初始化有一定影响。

你可能感兴趣的:(Unity启动耗时优化)