01. Unity优化指南

这是摘自Unity官方文档有关优化的部分,原文链接:https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity.html
总共分为如下系列:

  1. 采样分析
  2. 内存部分
  3. 协程
  4. Asset审查
  5. 理解托管堆 【推荐阅读】
    5.1 上篇:原理,临时分配内存,集合和数组
    5.2 下篇:闭包,装箱,数组
  6. 字符串和文本
  7. 资源目录
  8. 通用的优化方案
  9. 一些特殊的优化方案

采样分析

考虑到优化性能的时候,首先必须记住所有的优化是从采样分析开始。对应用进行采样发现问题所在是第一步,从而对采样的结果针对项目的代码和资源进行分析。

工具:

  • iOS:Instrument和XCode Frame Debugger
  • Android:Snapdragon Profiler
  • Intel CPU/GPU的平台:VTune和Intel GPA
  • PS4:Razor套装
  • Xbox:Pix工具

这些工具的使用条件是IL2CPP生成的项目,因为原生C++代码能够提供更加清晰的调用关系和方法调用的时间消耗,在Mono平台下做不到这些。

启动日志的分析

当观察启动时的跟踪日志,需要特别关注两个关键方法,这两个方法反映了项目中的配置、资源和代码如何影响启动时间。

游戏启动设置不同的平台上略有差异,在大部分平台上用户可以看到一个静态的启动界面。

启动日志

上图是来自于Instrument工具截取在iOS设备上运行实例工程的跟踪日志。在startUnity方法中,注意UnityInitApplicationGraphicsUnityLoadApplication方法。

UnityInitApplicationGraphics

这个方法会执行一系列的内部操作,设置图形显示设备和初始化Unity的内部系统。除此之外,它还会初始化资源系统,导入资源系统包含的所有文件的索引。

每个“Resources”目录中包含的资源文件都属于资源系统的数据。

【Resources”目录指的是“Assets”目录下的“Resources”以及这些“Resources”目录下的所有子文件夹。】

所以,如果Resources目录下的文件非常多,就会造成加载的时间比较长。

UnityLoadApplication

包含加载和初始化项目中的第一个场景。这个步骤包括反序列化和实例化场景中所有的数据,例如编译Shader,上传纹理和实例化GameObject。除此之外,第一个场景中的所有MonoBehaviour对象中的Awake方法都在这个方法内执行。

这些处理流程意味着,如果你的Awake回调方法中有耗时较长的代码,就会严重拖慢项目的启动速度。要不你就删除这段代码,要不你就把这段代码放到别的生命周期中执行。

运行日志的分析

在启动部分完成之后,接着就是PlayerLoop方法了。这是Unity的主循环,每帧都会执行一次。

运行日志

上图中的截图来自于Unity5.4的某个示例工程,展示了PlayerLoop中值得关注的几个方法。注意PlayerLoop中的方法在不同版本的Unity中可能不同。

PlayerRender

运行Unity的渲染系统,包括剔除不需要显示的对象,计算动态合批,给GPU提交绘制指令。图片特效或者基于渲染的脚本中的回调(如OnWillRenderObject)都会在这个函数中执行。一般情形下,当项目是可以UI交互的时候,这个方法会是消耗CPU资源的头号顾客。

BaseBehaviourManager

调用三种模板化的CommonUpdate方法。这些方法调用当前场景中活跃的GameObject上 MonoBehaviour中特定的回调方法。

  • CommonUpdate<UpdateManager> 调用Update回调
  • CommonUpdate<LateUpdateManager> 调用LateUpdate回调。
  • CommonUpdate<FixedUpdateManager> 调用FixedUpdate回调。

通常来讲,BaseBehaviourManager::CommonUpdate<UpdateManager>是最值得关注的方法,因为它是Unity工程中大部分代码的入口方法。

其他值得关注的代码:

UI::CanvasManager

如果工程用到了Unity UI,这个方法会调用几个不同的回调函数。包括Unity UI的批处理过程和布局更新;这两个操作会导致CanvasManager出现在剖析日志列表中。

DelayedCallManager::Update

运行协程。更多的细节可以参见系列文章中“协程”这一章。

PhysicsManager::FixedUpdate

运行PhysX物理系统,这个函数主要运行PhysX的内部代码,受到当前场景中物理组件个数的影响,如Rigidbody和Collider组件。还有,基于物理的回调也会出现在这个函数,如OnTriggerStay和OnCollisionStay方法。

如果工程中用到2D物理,在Physics2DManager::FixedUpdate方法下面会出现类似的方法。

脚本方法的分析

当IL2CPP转换后的代码被调用的时候,可以找到ScriptingInvocation对象。这是Unity的原生代码过渡到脚本运行环境下,执行脚本代码的关键部分。【从技术细节上来讲,当使用IL2CPP,C#的代码也会被转化成原生代码。然而编译后的代码通过IL2CPP运行框架执行方法的过程,和手写的C++代码执行过程不同】

脚本方法分析

上面的截图来自Unity5.4上的一个示例工程。RuntimeInvoker_Void行下面包含的所有函数都是交叉编译后的C#代码,这些方法每帧执行一次。

被转化后的方法命名规则是,每个方法是“类名_原始方法名”。在这些跟踪日志里面,有EventSystem.UpdatePlayerShooting.Update和一些其他的Update方法。这些是大多数MonoBehaviour类中的Update回调方法。

展开这些方法,很容易发现到底是哪些方法消耗了大部分的CPU时,也包括其他的脚本中的方法,如Unity的API和C# 库中的代码。

上面的跟踪日志展示了StandaloneInputModule.Process需要给整个UI系统投射射线,检测什么触碰事件正在悬停或者激活某个UI元素。最大的开销在于遍历所有的UI元素和检测鼠标的位置是否在这些UI元素的边界范围之内。

资源加载

资源加载也会出现在CPU的日志中。资源加载的主要方法是SerializedFile::ReadObject。这个方法通过执行Transfer方法把文件的二进制数据流和Unity的序列化系统连接起来。Transfer方法可以在所有的资源类型中找到,如纹理,MonoBehaviour和粒子系统。

资源加载

上面的截图展示了场景正在加载的日志。这个过程需要Unity去读取数据和反序列化场景中所有的资源,通过调用SerializedFile::ReadObject方法下的不同资源类型的Transfer方法。

通常来讲,如果在运行时间存在性能问题,而且SerializedFile::ReadObject方法占据了很大的性能开销的话,很有可能就是资源加载导致的帧率问题。注意,在大多数情况下,如果是 SceneManager,Resources和AssetBundle API这些方法发起同步资源加载的话,SerializedFile::ReadObject方法只能在主线程中看到。

这种性能问题可以通过常用的方法解决:使用异步加载,将ReadObject方法交给worker线程去做,或者提前加载比较大的资源。

注意,Transfer调用在克隆Object的时候也可能会出现,由CloneObject方法调用。如果CloneObject方法里面调用了Transfer方法,那么表明资源不是从存储器中加载,而是将老Object的数据传递给了新Object。Unity序列化老的Object并且根据这些数据反序列化出了新的Object。

下篇:内存分析

如果觉得文章对您有用,请点个赞呗!☺☺☺

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,079评论 25 707
  • 111. [动画系统]如何将其他类型的动画转换成关键帧动画? 动画->点缓存->关键帧 112. [动画]Unit...
    胤醚貔貅阅读 13,014评论 3 90
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,094评论 4 62
  • 好久么有回来住了,但是很安心。 我也不知道为什么在舒适的环境里我还是那么的闹心和不安,我没办法和你好好的相处,我控...
    简书2017阅读 187评论 0 0
  • 元组
    HFY_Code阅读 153评论 0 0