Unite优化总结(2017)
这是Unity官方技术专家伊恩的演讲总结:
Unity都要去遍历所有的组件来找到目标组件。每次都去查找是不必要的耗费,我们可以通过缓存的方式。
transform这样的属性内部还是调用函数。性能也是稍逊,频繁使用也用缓存。
遍历所有对象查找名字相符。可以在启动Awake, Start中缓存好。劣势是:有时候启动场景加载太慢也是因为太多Awake/Start函数中这样处理。
GameObject.FindWithTag可以能是个好主意。依据为对象设置的Tag。如果有比较多的Tag会有性能折扣。
编辑器Inspector注入更好。
Camera.main也是通过GameObject.FindWithTag实现的。在Canvas中最好指定eventCamera。否则它会调用到Camera.main, 又是个性能损耗。
GameObject.tag常用来比较对象的tag,但是直接采用.tag ==来进行对比的话,每一帧会产生GC Alloc。通过GameObject.CompareTag来进行比较则可以避免掉这些GC,但是前提是比较的tag需在Tag Manager中定义。
当调用ParticalSystem的Start,Stop,Pause,Clear,Simulate函数时,它会递归遍历子对象来找ParticalSystem组件。除非把第二个参数witchChildren设置为false。
Best Practice: 初始化阶段缓存所有要处理的组件。
这些C#函数是内部函数的包装。在2017.2版本之前这些函数实现是闭包,这也带来损耗。可以通过反射机制获取内部实现,通过Internal_Stop获取Stop(函数名前加Internal_)
当函数返回数组时返回的是一份完全的复制,这是为了避免弄坏函数内部的数据和状态。
比如Mesh.vertices Input.touches。可以如下处理:
Input.GetTouch()替代Input.touches
Physics.RaycastNonAlloc替代Physics.RaycastAll。其他的带All的函数都有这个No-GC替代版(从5.3版本开始)。
GetComponents,GetComponentsInChildren可以接受一个预分配存储空间的List。如果结果超过容量,会重新分配空间
Random Access->Array List
Add/Remove->Dictionary Hashset
Query->Dictionary
Dictionary is Hashset with many bucket linked to it. each bucket store all value has identical hash key. so go through it has overhead.
Update Manager Parttern。there are 3 requirements:
1: low-overhead iteration[Array, List]
2: constant time to insert/remove[Dictionary, List, Hashset]
3: constant time finish duplicate check[Dictionary, Hashset] silver bullet:multiple data structure. intrusive linked list
UnityEngine.Object-keyed Dictionaries
如果用UnityEngine.Object作为字典的键默认的比较函数是Object.Equal
内部调用UnityEngine.Object.CampareBaseObject
再内部调用C#的Object.ReferenceEqual函数
GetInstanceID()对于Mono来说内部会调用不安全函数(unsafe method)影响JIT优化能力。所以还不如用对象作为键值,解决办法是调用一次缓存InstanceID. IL2CPP就不会有不安全函数的困扰,性能总是稍高一点。
C#的以原始类型作为作为键值的字典是高度优化过的。
[MethodImplAttribute(MethodImplOptions.NoInlining)] 可以在 .Net 4.6 中使用了
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)] 就好像收到拷贝粘贴一样。
.Net4.6 以前的IL2CPP还没有实现这种AggressiveInlining。所以手动拷贝粘贴非常高频率的短代码是个好方法
property will generate method call(publid getter/setter function). so change property in hot path to public filed. maybe dirty, has performance profit
the base is canvas. collect the meshes and commit draw call
one ui element change can set canvas dirty. it will need regenerate mesh
recalculate layouts of auto-layouted elements
regenerate meshes for enabled elements. meshes are still created when alpha=0
regenerate materials to help batch meshes
On Canvas dirty one then dirty all. there are some exceptions:
change color property only dirty the vertics
change image fillAmount only dirty the vertics
UI sorted by depth. because ui commit to GPU transparent queue
sort means performance drops faster-than-linear as number of drawable elements rises
higher number of elements means a higher chance any given elements will change
use more canvas-the main tool fix ui batches
child canvas inherit rending settings:
maitain own geometry
perform own batch
pixel perfect is unnesscessary for scroll canvas. will be little fuzzy for single frame
pool scroll list item when scroll velocity is near zero, set it to zero. otherwise it will dirty the canvas and rebuild last for 2-3 seconds
collect raycat target UI element(Image, Text). check click point in rectangle
with default settings, only tests UI graphics
Performs point-rectangle insersection check for each RectTransform marked interactive. finish in a loop
Worldspace or ScreenSpace Camera canvas Blocking mask can select whether to cast against 2D/3D geometry Will then perform a 2D/3D raycast outwards from the Canvas' event camera Limited to camera's visible frustum
world space canvas must set event camera. otherwise it will access the Camera.main. Find object with tag. Find the object with tag set to MainCamera. has performance penalty. If there are many GameObject set with tag. Camera.main will be very costly property
Minimize the number of Graphic Raycaster. it will save ui system time.
Unity Layouts work on "dirty tag" system One Child Change will invalidate Layout that own the child
layout elements include Image, Text, Scroll Rect
ayout component: often parent gameobject of layout element
MarkLayoutForRebuild recursivly find Layout in parent GameObject. It Can Cost Linear-expand time
Every UI element that tries to dirty its layout will perform at least 1 GetComponents call even if didnot use layout system each time it marks its layout dirty
each layout group multiplies this cost Nested layout groups are particularly bad
OnEable/OnDisable
Reparenting->Twice Once for old parent, once for new parent
OnDidApplyAnimationProperties
OnRectTransformDimensionsChanged
Maximize number of dirty calls that will no-op
Disable first, then reparent into pool.
Removing from pool:Reparent first, then update data, then enable
Consider disabling the Canvas component(it will keep vertices buffer and so on), don't disable GameObject.
-Stops drawing the Canvas
-Does not trigger expensive OnDisable/OnEnable callbacks on UI hierarchy.
Be careful to disable child component that run expensive per-frame code!
Because they wouldn't auto Disable
Animators will dirty their elements every frame.
-Even if value in animation doesn't change!
-Animators have no no-op checks.
Animators are OK on things that always change
better use your own code or tweening system