前言
UnityProfiler
使用介绍
一种是editor下面的profiler
另一种是在目标平台上的profiler
细节
- GFX 通常指的是GPU的调用,所以可以不用太关心,想要关注的话专门切到GPU上去
- behaviour.update是比较常用和关心的项
batches
比如不同的物体使用相同的材质,
if you have multi objects of uniform scale using same materials,get batched in 1 draw call
memory 里面可以看到GC的频率,越低越好
例子
update里面的debug很耗费性能,在deep profiler里面,
Profiler.BeginSample(“test”);
//debug.log
Profiler.Endsample();
频率没有那么高的调用 每十帧调用一次
Framedebugger:
分析
CPU 85.95ms表示这一帧的总CPU时间
性能问题的原因
Vsync 垂直同步
这个一般不关心;
看到waitfortargetFPS的时候,意味着游戏正在等待Vsync,忽略即可;
ios强制执行vsync,其他平台可以关掉;
渲染分析
看看游戏的渲染的性能瓶颈主要在CPU还是GPU,方法是使用GPU分析器。(并不是所有设备都支持。)
开启GPU分析后,如果看到GPU时间超过CPU时间,则我们的性能瓶颈主要在GPU。
如果没法开启GPU,按时间排序,看gfx.waitforPresent在CPU中所占的时间最长,就是CPU在等待GPU
不管CPU还是GPU限制性能,分别进行优化。
GC
对于GC,查看该帧里面GC时间占整个帧时间的长度。
物理
常用的方法和思路:
- 碰撞层的设定layercollision matrix
1000绿色+1000红色,都只跟相同颜色的碰撞
在其中一个测试中,所有实例都属于Default层,并且通过字符串比较碰撞监听器上的游戏对象标签来完成交互。 在另一个测试中,每个对象类型都在它们自己的Layer上设置,我通过碰撞矩阵配置每个层的交互。 在这种情况下不需要进行字符串测试,因为只发生正确的冲突。
- 射线
坚决不要在fixedupdate里面发射线;
使用默认类型的碰撞体,而不是meshcollider
在射线函数上设定一个layer mask
- 不要尝试移动静态的碰撞器,最好加刚体。如果要通过代码控制移动,标记rigidbody为kinematic
慢脚本
注意的是:比如ImageEffects和OnwillRenderObject和OnPreCull函数是当作渲染数据,而不是脚本数据。
[unity性能优化]
性能分析\使用profile
UnityGC机制和优化
性能
unity优化和测试:
https://blog.csdn.net/qq_32418469/article/details/8132173
零容忍warning allocate
get component find object避免 比较慢
问题?
什么时候对象会被系统判定并且进行GC操作???怎么避免被释放掉内存
几个参考链接:
https://kb.cnblogs.com/page/106720/
https://www.jianshu.com/p/e597a74c4938
https://zhidao.baidu.com/question/172960970.html
Garbage Collect
基本概念
栈 用完释放 局部值类型
堆 定期释放
方法
用profiler分析,确定在哪些帧哪些函数出发了堆的重新分配
减少GC的三种方法
- 减少堆的分配和更少的对象引用,堆上对象越少,表示出发垃圾收集时候,进行的检测越少
- 在性能关键时刻,减少堆的分配和解除分配
- 对GC和堆扩展进行计时,以便让他们在可预测和方便的时间
减少创建的垃圾量
不要在update里频繁创建对象。
- 高速缓存
1 | void OnTriggerEnter(Collider other) |
- 不要分配频繁调用的函数
1 | void Update() |
或者给调用设定计时器,不要每帧调用
1 | void Update() |
- 清空集合
对于collections,在创建集合或者调整集合大小时候才会分配1
2
3
4
5
6
7
8
9
10
11
12
13void Update()
{
列出myList = new List();
PopulateList(myList中);
}
//>>>
private List myList = new List();
void Update()
{
myList.Clear();
PopulateList(myList中);
}
- 对象池
略
不必要的常见堆内存分配
字符串
字符串的操作会带来大量的gc,下面例子减少在update里操作字符串的次数
1 | public Text timerText; |
unity函数调用
在这种情况下减少分配很容易:我们可以简单地缓存对数组的引用。当我们这样做时,只创建一个数组,并相应减少创建的垃圾量。以下代码演示了这一点。在这种情况下,我们在循环运行之前调用Mesh.normals并缓存引用,以便只创建一个数组。
1 | void ExampleFunction() |
另外一个例子
在以下示例代码中,通过调用GameObject.tag创建垃圾:1
2
3
4
5private string playerTag =“Player”;
void OnTriggerEnter(Collider other)
{
bool isPlayer = other.gameObject.tag == playerTag;
}
如果我们使用GameObject.CompareTag(),则此函数不再生成任何垃圾:1
2
3
4
5private string playerTag =“Player”;
void OnTriggerEnter(Collider other)
{
bool isPlayer = [other.gameObject.CompareTag](http://other.gameobject.comparetag/)(playerTag);
}
GameObject.CompareTag不是唯一的; 许多Unity函数调用具有不会导致堆分配的替代版本。例如,我们可以使用Input.GetTouch()和Input.touchCount代替Input.touches,或使用Physics.SphereCastNonAlloc()代替Physics.SphereCastAll()。
Boxing
1 | void ExampleFunction() |
协程
产生GC
yield return 0;
不产生GC
yield return null;
不好的例子,每次循环迭代时创建waitforseconds对象:1
2
3
4while (!isComplete)
{
yield return new WaitForSeconds(1f);
}
缓存并重用waitforseconds对象可以减少垃圾:
1 | WaitForSeconds delay = new WaitForSeconds(1f); |
对于需要使用协程的地方,通常有两个代替方案:
- 跟时间相关的,在update里面监视时间
- 跟顺序逻辑相关的,我们优先使用对象之间的消息机制
foreach循环
旧版本
函数引用
不管匿名的还是命名的函数,都是引用类型
LINQ and Regular Expressions
优化代码
- 下面是值类型,但是引用了string,所以整个结构体的数组也会被GC在运行时检查。
1 | public struct ItemData |
- 尽量减少对对象的引用:
1 | public class DialogData |
强制进行GC处理
System.GC.Collect();
在重要节点前进行GC处理
参考链接