Unity 依赖于 CPU(针对其 SIMD 部分进行了大量优化,如 x86 上的 SSE 或 ARM 上的 NEON)执行蒙皮、批处理、物理、用户脚本、粒子等。
GPU 用于着色器、绘制调用、图像效果。
绝大多数问题 (80%) 是由几个主要原因 (20%) 产生的。可使用 Editor 性能分析器识别处理器密集程度最高的函数调用并首先对它们进行优化。通常,优化一些主要函数可以显著提高整体执行速度。
应确保仅在必要时才执行函数。例如,可使用 OnBecameVisible 和 OnBecameInvisible 等事件来检测无法看到对象的情况并避免更新该对象。可使用协程来调用需要定期更新但不需要每帧运行的代码:
// 每帧执行一些操作:
void Update () {
}
// 每 0.2 秒执行一些操作:
IEnumerator SlowUpdate () {
while (true) {
// 执行操作
yield return new WaitForSeconds (0.2f);
}
}
可使用 .NET System.Threading.Thread 类在单独的线程上运行繁重的计算。因此可在多核上运行,但请注意,Unity API 不具有线程安全性;您需要缓冲输入和结果,并在主线程上读取和分配它们以便使用 Unity API 调用。
并非所有用户代码都显示在性能分析器中。但可使用 Profiler.BeginSample 和 Profiler.EndSample 使所需的用户代码显示在性能分析器中。
Unity Editor 性能分析器目前无法显示 GPU 数据。我们正在与硬件制造商合作,使 Tegra 设备率先出现在 Editor 性能分析器中。
PowerVR 是基于区块的延迟渲染器,因此不可能获得每个绘制调用的 GPU 时间。但是,可使用 Unity 的内置性能分析器(将结果打印到 Xcode 输出的性能分析器)获得整个场景的 GPU 时间。Apple 的工具目前只能显示 GPU 及其部件的繁忙程度,而不能给出以毫秒为单位的时间。
PVRUniSCo 提供整个着色器的周期,并估算着色器代码中每一行的 周期。Windows 和 Mac!但是,该工具始终无法与 Apple 的驱动程序做到完全匹配。仍然可作为一种很好的估算手段。
在 Tegra 上,NVIDIA 提供了出色的性能工具,可提供您所需的所有数据(每次绘制调用的 GPU 时间、每个着色器的周期数、强制 2x2 纹理、空视图矩形),并可在 Windows、OSX 和 Linux 上运行。PerfHUD ES 不易与消费类设备配合使用,需要使用 NVIDIA 的开发板。
Qualcomm 提供出色的 Adreno Profiler(仅限 Windows),仅适用于 Windows,但可与消费类设备配合使用!该工具提供时间轴图、帧捕获、帧调试、API 调用、着色器分析器和实时编辑功能。
内部性能分析器可提供每个模块的概况:
Unity Profiler 使用的端口:
应可从网络节点内访问这些端口。也就是说,您尝试分析性能的设备应能够在装有启用了性能分析器的 Unity Editor 的计算机上看到这些端口。
有两种类型的内存:Mono 内存和 Unity 内存。
Mono 内存处理脚本对象以及 Unity 对象(游戏对象、资源、组件等)的包装器。当分配量超出可用内存或执行 System.GC.Collect() 调用时,垃圾回收器会进行清理。
内存以堆块形式分配。如果不能使数据放入分配的块,则可以分配更多内存。在应用程序关闭之前,堆块将保留在 Mono 中。换句话说,Mono 不会释放用于操作系统的任何内存 (Unity 3.x)。一旦分配了一定数量的内存,该内存就会保留给 Mono,而不再可用于操作系统。即使释放内存后,也只能在内部用于 Mono,而不能用于操作系统。性能分析器中的堆内存值只会增加,永不减少。
如果系统无法将新数据放入已分配的堆块,则 Mono 会调用“GC”并可分配新的堆块(例如,由于碎片化)。
“太多堆区段”意味着已经耗尽了 Mono 内存(因为碎片化或大量使用)。
使用 System.GC.GetTotalMemory
可获取使用的 Mono 内存总量。
一般的建议是,使用尽可能小的分配。
Unity 内存用于存储资源数据(纹理、网格、音频、动画等)、游戏对象、引擎内部数据(渲染、粒子、物理等)。
使用 Profiler.usedHeapSize
可获取使用的 Unity 内存总量。
尚无工具,但可使用以下工具。
您也可以使用 Unity API 调用来创建自己的工具:
FindObjectsOfTypeAll (type : Type) : Object[]
FindObjectsOfType (type : Type): Object[]
GetRuntimeMemorySize (o : Object) : int
GetMonoHeapSize
GetMonoUsedSize
Profiler.BeginSample/EndSample
- 对您自己的代码进行性能分析UnloadUnusedAssets () : AsyncOperation
System.GC.GetTotalMemory/Profiler.usedHeapSize
对已加载对象的引用 - 没有办法计算此数据。一种变通方法是针对公共变量“查找场景中的引用”。
OnGUI()
:它会每帧发出几次,完全重绘视图,并生成大量需要调用垃圾收集的内存分配调用。System.GC.Collect()
:允许暂时性中断时,可使用此 .Net 函数。在某些时候,游戏可能会因“内存不足”而崩溃,但理论上内存是足够的。发生这种情况时,请比较正常游戏内存占用量和崩溃发生时分配的内存大小。如果数字相差较大,则表示存在内存峰值。这可能是由于: