48

Unity Profiler 真机调试以及函数耗时案例分享-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/288274
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
最近实在是太忙了,一直想保持定量的更新频繁,但无奈当下的工作量实在太多了,只能先去处理手头上的工作,趁周末将最近应用Profiler工具解决的一个关于函数耗时的案例分享出来 

有些日子没做优化相关的工作,Profiler也许久没有使用,有些生疏,所以文中有误的地方,还请指正

下面先说一下如何使用Unity Profiler在真机上进行调试,我在项目中使用的Unity版本是5.6.5p4

UnityProfiler官方指南
https://docs.unity3d.com/Manual/Profiler.html

连接真机调试的方式有两种:

1、WIFI--保证PC和手机在同一个网络中

2、ADB--连接上手机,通过ADB命令连接

两种方法都OK,我在测试的时候,两者同时使用,如果网络不太稳定,我会选择ADB的形式

第一种方式:WIFI

1) 保证手机正确的连接到电脑,在Android Studio的Terminal下输入命令:
->adb devices

List of devices attached

xxxxxxxx device

说明手机已经连接成功,如果连接不成功,可以Kill掉adb或是重启Android Studio,还有一种情况可能会连接失败,就是ADB的版本太低,升级后尝试

如果我启动了模拟器,会输出如下:
List of devices attached
xxxxxxx        device
emulator-5554   device

这时候使用adb命令的时候,要记得指定设备

如果 adb命令不识别,需要将adb的路径加入到环境变量中。

5be676ba54048.png



(这里再吐槽一句,之前我在使用Android Studio时,一直因为墙的原因,导致很多库无法更新下来,即便是开了VPN,也会出现问题,公司里的网络默认是通过VPN进行访问的,速度很快,
Android Studio也非常的流畅的更新了下来,VPN和VPN之间还是有蛮大的区别的,当时为省钱
去用一些便宜的VPN,导致给自己添了很多的麻烦,在效率面前,多花钱能提高的,都是值得付出的)

2)通过adb判断真机正确连上机器以后,下一步设置Unity工程(前提保证可以正常的导出APK,SDK,JDK,NDK正确配置)

5be678581e491.png

这里使用Gradle来进行构建,并选中Development Build和Autoconnect Profiler

打开Player Settings:

5be678ee7bd39.png

勾选Enable Internal Profiler

然后点击Build And Run,构建完成,打开Unity Profiler,等待真机启动,
稍等片刻,Unity Profiler就可以获取真机上的性能数据了。

5be67a81e558d.png

点击Active Profiler可以看到当前连接的真机Xiaomi_MI_NOTE_Pro@xxx

5be67ab6ae4be.png

网络环境不好的情况下,这里会出现延迟,即Unity Profiler捕获不到真机的数据,也没有可用的Active Profiler,可以稍等片刻,如果一直没有,重启Unity:)

在测试中,可以通过点击Record来进行记录(默认是开启的),再次点击可以取消Record,这样我们可以针对之前Record的数据进行分析

第二种方式 :ADB

看上图,Active Profiler->AndroidPlayer(ADB&127.0.0.1:34999)这是通过ADB的形式连接真机调试,如果没出现这个选项,可以在Terminal下输入adb命令:

adb forward tcp:34999(上图显示的端口号) localabstract:Unity-包名

这里要注意防火墙是否屏蔽掉了54998~55511端口,最好关闭防火墙。

在实际的测试过程中,比如我要测试某个场景的Profiler性能数据,方便我们操作和定位,可以先增加一个空的Scene,然后启动后,确保Profiler已经连接上真机了,待命状态,这时候,手动的点击按钮切换到我们需要测试的Scene下

以上就是Unity Profiler连接真机调试的操作过程,下面分享一个最近通过Unity Profiler定位到的一个函数耗时的案例

场景:
接手CP的一个项目,发现在中低端机上,加载耗时过长,通过定位发现其中一个Http请求的操作引起GC Alloc并耗时过长(测试的机器是小米Note,性能尚可,所以显示的消耗并不大,在中低端设备上消耗比较明显)。

我对项目代码并不熟悉,时间又比较紧迫,所以只能通过Profiler采样,一步一步的缩小问题的范围,直到找到消耗热点的最终位置。

下面是在第一个场景在加载时,Profiler的情况:

5be689ea63fe1.png

右上角的蓝色区域是消耗峰值,我们用鼠标定位当前的帧(用键盘更好定位),会在Overview下列出当前帧的函数消耗,我们点击GC Alloc进行排序,发现HttpUpdateDelegator.Update()占了79.6%的消耗,而当帧的总消耗是83.1%,所以问题直接就可以定 位到HttpUdateDelegator.Update()方法中。

打开HttpUpdateDelegator.cs发现,这是Best Http(pro)插件的源码,所以初步确认,这种消耗应该不是插件本身的,因为BestHttp在许多项目中应用过,性能缺陷不至于在这里,应该是CP使用上的问题,继续定位到Update方法:

5be68bcc18a95.png

在Update方法中,可以看到最下面那行代码:
HttpManager.OnUpdate()

所以我们继续进入这个OnUpdate方法中:

OnUpdate代码太多,贴上来太影响阅读,贴上一个简图:
5be68dc236b08.png

在HttpManager.cs中,维护一个当前活动连接对象的List:

private static List<ConnectionBase> ActiveConnections = new List<ConnectionBase>();

在OnUpdate中遍历ActivieConnections,并对不同的HTTPConnectionStates状态进行处理,因为
无法确定具体是哪些case的消耗,所以我们可以对每一个case进行采样,以获取更准确的数据。

比如像下面这样:
5be68ee7dbb15.png

这样我们再次打包在真机上进行Unity Profiler调试。

5be6927214773.png

经过测试,最终问题定位在HttpUpdate_WaitForRecycle这个采样下,在WaitForRecycle状态下只有几行代码:

5be692eb66e45.png

我们可以分别为三个方法,再继续进行采样,最终问题定位在:conn.HandleCallback()这里:

5be693a0119fa.png

对HandleCallBack继续通过采样定位,最终定位在CurrentRequest.CallCallback()这一行,
这是一个Delegate,性能的消耗是在这个Delegate中。

所以,我们只需要知道,这个Delegate是哪个类中的哪个方法来实现的就可以了。

通过Debug调试,最终确定如下:

5be695018d10b.png

在TimeController类中实现了Delegate,最终性能消耗点定位如下:

5be695ac67b93.png

将Http请求回来的时区数据(数据量特别小),通过Newtonsoft.Json库进行反序列化。

Newtonsoft.Json是第三方的Json解析库

https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json.htm

Newtonsoft.Json在移动端消耗太大,我们可以使用Unity提供的JsonUtility替代Newtonsoft.Json即可。

只改一行代码即可:
_res = JsonUtility.FromJson<TimeZoneResponse>(_response.DataAsText);

这样我们再打包进行Unity Profiler调试,性能热点消失,GC Alloc为0,Time ms 小到可以忽略不计。

这里还有一点需要注意,最终定位到Callback,因为是Delegate,所以这里不一定只有TimeController一处,也可能会被其它的逻辑处理(我在后面又发现了2次相同的情况),并使用到了Newtonsoft的Json库,所以这种情况要再仔细的确认OK

这个项目中大量的使用到了newtonsoft,全部替换掉需要不少的工作量,CP采用这种方式可能是因为原生JsonUtility不直接支持List和Dic的序列化,不过也是有办法可以解决的

常见问题:
1.如果发现Unity Profiler连接不上真机,请查看PlayerSettings中打出来的是不是
Development Build版本并且勾选了Autoconnector Profiler,在PlayerSettings中,也要记得开  启是Release是不可以的

2.连接方式有ADB和WIFI两种,ADB更稳定,AndroidPlayer@xxxx:xxx就是ADB连接的形式,确保了上面问题1的情况OK,还是无效,建议重启Unity试试

最近在研究冷启动(cold start),也是比较有趣的地方,稍后会分享上来。

分享就到这里,感谢您的阅读,周末愉快~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK