Unity-Memory-Basic

前言

UnityProfiler

使用介绍

一种是editor下面的profiler

另一种是在目标平台上的profiler

细节

  1. GFX 通常指的是GPU的调用,所以可以不用太关心,想要关注的话专门切到GPU上去
  2. 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:

性能分析的文章
https://unity3d.com/learn/tutorials/temas/performance-optimization/diagnosing-performance-problems-using-profiler-window?playlist=44069

分析

CPU 85.95ms表示这一帧的总CPU时间

性能问题的原因

Vsync 垂直同步

这个一般不关心;

看到waitfortargetFPS的时候,意味着游戏正在等待Vsync,忽略即可;

ios强制执行vsync,其他平台可以关掉;

渲染分析

看看游戏的渲染的性能瓶颈主要在CPU还是GPU,方法是使用GPU分析器。(并不是所有设备都支持。)

  1. 开启GPU分析后,如果看到GPU时间超过CPU时间,则我们的性能瓶颈主要在GPU。

  2. 如果没法开启GPU,按时间排序,看gfx.waitforPresent在CPU中所占的时间最长,就是CPU在等待GPU

不管CPU还是GPU限制性能,分别进行优化。

GC

对于GC,查看该帧里面GC时间占整个帧时间的长度。

物理

常用的方法和思路:

  1. 碰撞层的设定layercollision matrix

1000绿色+1000红色,都只跟相同颜色的碰撞

在其中一个测试中,所有实例都属于Default层,并且通过字符串比较碰撞监听器上的游戏对象标签来完成交互。 在另一个测试中,每个对象类型都在它们自己的Layer上设置,我通过碰撞矩阵配置每个层的交互。 在这种情况下不需要进行字符串测试,因为只发生正确的冲突。

  1. 射线

坚决不要在fixedupdate里面发射线;

使用默认类型的碰撞体,而不是meshcollider

在射线函数上设定一个layer mask

  1. 不要尝试移动静态的碰撞器,最好加刚体。如果要通过代码控制移动,标记rigidbody为kinematic

慢脚本

注意的是:比如ImageEffects和OnwillRenderObject和OnPreCull函数是当作渲染数据,而不是脚本数据。

[unity性能优化]

性能分析\使用profile

https://unity3d.com/learn/tutorials/temas/performance-optimization/diagnosing-performance-problems-using-profiler-window?playlist=44069

UnityGC机制和优化

https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games

性能

https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html?_ga=2.201618392.833542919.1553218124-1934737889.1507532948

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. 高速缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void OnTriggerEnter(Collider other) 
{
​ Renderer [] allRenderers = FindObjectsOfType <Renderer>();
​ ExampleFunction(allRenderers);
}
//>>>
void Start()
{
​ allRenderers = FindObjectsOfType <Renderer>();
}
void OnTriggerEnter(Collider other)
{
ExampleFunction(allRenderers);
}
  1. 不要分配频繁调用的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Update()
{
​ ExampleGarbageGeneratingFunction(transform.position.x);
}
>>>

private float previousTransformPositionX;
void Update()
{
float transformPositionX = transform.position.x;
if(transformPositionX!= previousTransformPositionX)
​ {
​ ExampleGarbageGeneratingFunction(transformPositionX);
​ previousTransformPositionX = transformPositionX;
​ }
}

或者给调用设定计时器,不要每帧调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Update() 
{
​ ExampleGarbageGeneratingFunction();
}

>>>
private float timeSinceLastCalled;
private float delay = 1f;
void Update()
{
​ timeSinceLastCalled + = Time.deltaTime;
if(timeSinceLastCalled>DelayTime)
​ {
​ ExampleGarbageGeneratingFunction();
​ timeSinceLastCalled = 0f;
​ }
}
  1. 清空集合

对于collections,在创建集合或者调整集合大小时候才会分配

1
2
3
4
5
6
7
8
9
10
11
12
13
void Update()
{
​ 列出myList = new List();
​ PopulateList(myList中);
}

//>>>
private List myList = new List();
void Update()
{
​ myList.Clear();
​ PopulateList(myList中);
}

  1. 对象池

不必要的常见堆内存分配

字符串

字符串的操作会带来大量的gc,下面例子减少在update里操作字符串的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Text timerText; 
private float timer;
void Update()
{
​ timer += Time.deltaTime;
​ timerText.text = "TIME:" + timer.ToString();
}

>>>
public Text timerHeaderText;
public Text timerValueText;
private float timer;
void Start()
{
​ timerHeaderText.text = "TIME:";
}
void Update()
{
​ timerValueText.text = timer.toString();
}

unity函数调用

在这种情况下减少分配很容易:我们可以简单地缓存对数组的引用。当我们这样做时,只创建一个数组,并相应减少创建的垃圾量。以下代码演示了这一点。在这种情况下,我们在循环运行之前调用Mesh.normals并缓存引用,以便只创建一个数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ExampleFunction()
{
for(int i = 0; i <myMesh.normals.Length; i ++)
​ {
​ Vector3 normal = myMesh.normals [i];
​ }
}
void ExampleFunction()
{
​ Vector3 [] meshNormals = myMesh.normals;
for(int i = 0; i <meshNormals.Length; i ++)
​ {
​ Vector3 normal = meshNormals[i];
​ }
}

另外一个例子

在以下示例代码中,通过调用GameObject.tag创建垃圾:

1
2
3
4
5
private string playerTag =“Player”; 
void OnTriggerEnter(Collider other)
{
bool isPlayer = other.gameObject.tag == playerTag;
}

如果我们使用GameObject.CompareTag(),则此函数不再生成任何垃圾:

1
2
3
4
5
private 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
2
3
4
5
void ExampleFunction() 
{
int cost = 5;
string displayString = String.Format("Price: {0} gold", cost);
}

协程

产生GC
yield return 0;
不产生GC
yield return null;

不好的例子,每次循环迭代时创建waitforseconds对象:

1
2
3
4
while (!isComplete) 
{
yield return new WaitForSeconds(1f);
}

缓存并重用waitforseconds对象可以减少垃圾:

1
2
3
4
5
WaitForSeconds delay = new WaitForSeconds(1f); 
while (!isComplete)
{
yield return delay;
}

对于需要使用协程的地方,通常有两个代替方案:

  1. 跟时间相关的,在update里面监视时间
  2. 跟顺序逻辑相关的,我们优先使用对象之间的消息机制

foreach循环

旧版本

函数引用

不管匿名的还是命名的函数,都是引用类型

LINQ and Regular Expressions

优化代码

  1. 下面是值类型,但是引用了string,所以整个结构体的数组也会被GC在运行时检查。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public struct ItemData 
{
public string name;
public int cost;
public Vector3 position;
}

private ItemData[] itemData;

//>>>单独定义,这样只检查string数组对象,而不是所有的都检查

private string[] itemNames;
private int[] itemCosts;
private Vector3[] itemPositions;
  1. 尽量减少对对象的引用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DialogData 
{
private DialogData nextDialog;
public DialogData GetNextDialog()
{
return nextDialog;
​ }
}

//>>>
public class DialogData
{
private int nextDialogID;
public int GetNextDialogID()
{
return nextDialogID;
​ }
}

强制进行GC处理

System.GC.Collect();
在重要节点前进行GC处理

参考链接

// https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games //