Unity 代码优化记录
文档
unity 代码优化分析:https://docs.unity3d.com/Manual/analysis.html
Unity 修复性能问题:https://learn.unity.com/tutorial/fixing-performance-problems-2019-3?courseId=5c87de35edbc2a091bdae346#604586d7edbc2a5b4345d249
实例
具体枚举转Enum类型会有20B的GC
-
考虑判断原来一致不重复赋值
-
考虑使用整形存储代替枚举
减少使用Enum.TryParse
- 频繁调用考虑用字典存起来
避免频繁调用匿名函数
GC 闭包和匿名方法
- 减少闭包和匿名函数使用频率。
Shader属性用ID代替字符串
Shader.PropertyToID
- 动画key 和 材质属性考虑初始化先转成对应ID,调用时使用ID。
减少字符串拼接
Repeated string concatenation
- 重复的拼接,提前初始化,考虑static,所有组件公用。
减少GameObject.Find使用
Find 和 FindObjectOfType
干掉高耗时,但没有实际影响的物理计算
- 因为demo中物理引擎只用计算网格相交,不需要重力,受力,速度等参数,所以不需要计算速度约束和位置约束等。考虑增加开关控制。
减少查表频率和提前初始化List
-
常量List避免每次计算new
-
读表避免频繁调用原始接口重复查找
减少 int32.tostring()
- 避免循环里重复调用相同耗时操作
用GPU Instance 减少DrawCall
GPU Instance 深入浅出 知乎
https://docs.unity.cn/cn/current/Manual/GPUInstancing.html
- demo中血条搞了个类似液体晃动,怪物很多,没用GUP Instance的话,每个怪物都要调一次DrawCall,相同材质和网格的怪物可以合并成一次DrawCall,需要调整shader和C#代码。
shader修改
C#修改
- Renderer.GetMaterial() 可以查看源码
GraphicesScriptBindings.cpp
中GetAndAssignInstantiatedMaterial
结果对比
- 合批前,每个怪物血液一次DrawCall,合并后所有怪物一次DrawCall。
公共只读字段改为静态
公共只读字段,用const或static readonly
HashSet代替List循环查找、或判断
字典等容器复用
为尽量减少容器new带来的gc以及碎片化内存,部分场景可以循环利用容器。
需注意容器复用应尽量在相同使用场景复用,不同使用场景需求的容器大小不同可能造成容器频繁Resize或产生冗余分配。
当然在容量大小确定的情况下使用Native容器作为替代方案也是可以的。
怪物创建峰波耗时处理
对于短时间内进行大量对象的创建等耗时逻辑,采取的优化方案一般是:分线程处理和分帧处理。
比如短时间内创建大量怪物,采取了分帧处理,限制了每帧最大创建怪物数量。
字典Key 用int代替String
- 提前计算
String.GetHashCode()
,并作为字典key。需要注意可能存在哈希冲突,在分配key时需要处理冲突。
用数组代替字典
-
快个三四倍。
-
GetCom<T>()
实体组件原来是传Type类型,底下是Dictionary<Type, XXX>,这个RuntimeType.GetHashCode()
也比较耗时。因为组件数量有限,所以直接分配一个数组,每个组件占一个索引,取得时候用int索引取组件GetCom<T>(int TypeID)
。 -
下图红色框。
字典字缓存成员变量
- 频繁取的,但又修改少的,比如怪物移动速度,自己缓存个成员变量。下图绿色框。
属性改用成员变量
-
上图白框。
-
通常不考虑极端性能时还是推荐用属性。属性编译后实际成了方法,多一层调用堆栈。
-
也可以使用内敛方式
[MethodImpl(MethodImplOptions.AggressiveInlining)]
,把属性代码编译到调用处。