Unity3D 进阶教程 - 13 代码性能优化

概述

前两篇专注于渲染和内存两个硬件的优化。这一篇回到 代码本身——同样的逻辑,不同的编码方式性能差距可达 10 倍以上。

代码性能优化的核心原则:选择正确的数据结构和算法,然后用编译器友好的方式编写。

1. 数据结构选择

1.1 List vs Array vs HashSet vs Dictionary

操作 List Array HashSet Dictionary
顺序遍历(10K 元素) 0.02ms 0.02ms 0.10ms 0.10ms
查找(1K 次) 15ms(O(n)) 15ms 0.02ms(O(1)) 0.02ms(O(1))
内存 最低

选择建议

1
2
3
4
5
6
7
8
// 只需要顺序访问 -> Array 或 List
var items = new[] { 1, 2, 3 };

// 需要快速查找 + 不重复 -> HashSet
var activePlayers = new HashSet<Player>();

// 需要 Key-Value 快速查找 -> Dictionary
var playerScores = new Dictionary<string, int>();

1.2 使用 NativeContainer 和 Burst

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;

[BurstCompile]
public struct UpdateBulletJob : IJobParallelFor
{
public NativeArray<Vector3> positions;
public NativeArray<Vector3> velocities;
public float deltaTime;

public void Execute(int index)
{
positions[index] += velocities[index] * deltaTime;
}
}

2. 字符串优化

1
2
3
4
5
6
7
8
9
10
11
12
13
// 慢:每次 += 产生新字符串
string log = "";
for (int i = 0; i < 1000; i++)
log += i + ", ";

// 快:StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
sb.Append(i).Append(", ");

// 利用 ReadOnlySpan 避免子串分配
ReadOnlySpan<char> span = "Hello, World!";
var sub = span[7..12]; // 不分配

3. 循环优化

3.1 缓存友好遍历

1
2
3
4
5
6
7
// 慢:逐字段遍历,数据不连续
for (int i = 0; i < count; i++)
{
// positions[i], velocities[i] 分别在不同内存区域
}

// 快:结构体数组,连续存储,一次缓存加载多个元素

3.2 减少循环内部分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 慢:循环内有分支
for (int i = 0; i < enemies.Count; i++)
{
if (enemies[i].isActive)
enemies[i].UpdateAI();
}

// 快:先过滤再处理
var active = new List<Enemy>();
for (int i = 0; i < enemies.Count; i++)
if (enemies[i].isActive) active.Add(enemies[i]);

for (int i = 0; i < active.Count; i++)
active[i].UpdateAI();

4. GetComponent 与反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 慢:每帧 GetComponent
void Update()
{
var rigidbody = GetComponent<Rigidbody>();
rigidbody.AddForce(Vector3.up);
}

// 快:缓存
private Rigidbody _rigidbody;

void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
}

void Update()
{
_rigidbody.AddForce(Vector3.up);
}

5. 性能对照表

操作 相对速度
int x = 0 ~0.3 ns
obj.Method() ~3 ns
interface.Method() ~8 ns
delegate.Invoke() ~15 ns
GetComponent() ~200 ns
FindObjectOfType() ~2 us
Instantiate() ~500 us

总结

  • 数据结构选型 是性能的基石
  • 字符串是性能杀手,StringBuilder 和 ReadOnlySpan 是主要工具
  • 循环优化 合并分支、缓存友好
  • 缓存一切 GetComponent 结果
  • 只在 Profiler 确认的瓶颈处优化,不要提前优化

下一篇将切入 ECS 实体组件系统,进入 Unity 数据驱动架构的实战领域。

ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
分享: