Unity3D 进阶教程 - 15 Job System 与 Burst Compiler

概述

现代 CPU 拥有 4-16 个核心,但 Unity 传统的 MonoBehaviour.Update 只在单线程上执行,其他核心大部分时间空闲。

Job System 让开发者安全地将工作分发到多个线程。Burst Compiler 则将 C# 代码编译为高度优化的原生机器码(利用 SIMD 指令集)。两者结合,可以将 CPU 密集型任务加速 10-50 倍。

1. 为什么需要 Job System

1
2
3
4
5
6
7
8
9
// 传统单线程:所有 Update 依次执行
void Update()
{
for (int i = 0; i < particles.Length; i++)
{
particles[i].Position += particles[i].Velocity * Time.deltaTime;
}
// 这段代码只用了一个核心
}

Job System 将工作切分为小块,分发到所有可用核心:

1
2
3
4
5
6
7
8
9
var job = new UpdateParticleJob
{
positions = positions,
velocities = velocities,
deltaTime = Time.deltaTime
};

var handle = job.Schedule(particlesCount, 128);
handle.Complete();

2. Job 基础

2.1 IJobParallelFor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;

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

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

public class JobExample : MonoBehaviour
{
private NativeArray<Vector3> _positions;
private NativeArray<Vector3> _velocities;
private const int Count = 100000;

private void Start()
{
_positions = new NativeArray<Vector3>(Count, Allocator.Persistent);
_velocities = new NativeArray<Vector3>(Count, Allocator.Persistent);
}

private void Update()
{
var job = new UpdatePositionJob
{
positions = _positions,
velocities = _velocities,
deltaTime = Time.deltaTime
};

var handle = job.Schedule(Count, 64);
handle.Complete();
}

private void OnDestroy()
{
_positions.Dispose();
_velocities.Dispose();
}
}

2.2 依赖链

多个 Job 可以串联,形成依赖链:

1
2
3
4
var velocityHandle = velocityJob.Schedule(count, 64);
var positionHandle = positionJob.Schedule(count, 64, velocityHandle);
var collisionHandle = collisionJob.Schedule(count, 64, positionHandle);
collisionHandle.Complete();

3. Burst Compiler

只需要在 Job 结构体上加 [BurstCompile]

3.1 性能对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 普通 C# 循环(1M 次 sin 计算)
for (int i = 0; i < 1000000; i++)
result[i] = Mathf.Sin(i * 0.001f);
// 耗时:~45ms

// Burst 优化
[BurstCompile]
public struct SinJob : IJobParallelFor
{
public NativeArray<float> results;
public void Execute(int index)
{
results[index] = math.sin(index * 0.001f);
}
}
// 耗时:~3ms(15 倍提升)

3.2 Burst 的限制

1
2
3
4
5
6
7
8
9
10
11
12
不能使用:
- 引用类型(class、string、object)
- 托管数组(T[])
- 异常(try-catch)
- 委托(Action、Func)
- 反射
- 虚方法调用

可以使用:
- 值类型(struct、enum、int、float)
- NativeContainer
- Unity.Mathematics 类型(float3、quaternion)

4. 性能基准

操作 单线程 C# Job + Burst 提升
100K 粒子更新 ~3.5ms ~0.3ms 11x
500K 向量计算 ~18ms ~0.8ms 22x
100K AABB 碰撞检测 ~8ms ~0.4ms 20x
1M sin 计算 ~45ms ~3ms 15x

总结

  • Job System 将工作分配到多核 CPU,依赖链自动管理执行顺序
  • Burst Compiler 将 Job 编译为高效机器码,性能提升 10-50 倍
  • NativeContainer 是 Job 的唯一数据容器,需手动管理生命周期
  • 适合:大量并行计算、物理模拟、粒子系统、路径查找

下一篇将进入 Netcode for GameObjects,实现 Unity 多人游戏的网络通信。

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