๐ English Abstract
This technical article explores how to eliminate main thread performance bottlenecks during large-scale NPC pathfinding in Unity by combining the C# Job System and Burst Compiler. It provides a step-by-step troubleshooting guide for resolving NativeArray memory leaks, managing native container lifecycles, and handling edge cases in multi-threaded environments. Readers will gain actionable engineering insights into profiling, job dependency management, and high-performance data-oriented programming.
์คํ์๋ ๊ฒ์์ด๋ ๋๊ท๋ชจ RTS(Real-Time Strategy) ์ฅ๋ฅด๋ฅผ ๊ฐ๋ฐํ ๋ ๊ฐ์ฅ ํํ ์ง๋ฉดํ๋ ๊ธฐ์ ์ ๋์ ์ค ํ๋๋ ์๋ฐฑ์์ ์์ฒ ๊ฐ์ ๋ฌํ๋ NPC(Non-Player Character)์ ์ค์๊ฐ ๊ธธ์ฐพ๊ธฐ ์ฐ์ฐ(Pathfinding Computation)์
๋๋ค. ์ ๋ํฐ์ ๊ธฐ๋ณธ ์์คํ
์ธ NavMeshAgent๋ ์์ฌ์ด ์ฌ์ฉ์ฑ์ ์ ๊ณตํ์ง๋ง, ๋จ์ผ ์ค๋ ๋ ๊ตฌ์กฐ์ ์์กดํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด ์๊ฐ ๋์ด๋๋ฉด ๋ฉ์ธ ์ค๋ ๋ ๋ณ๋ชฉ(Main Thread Bottleneck)๊ณผ ํ๋ ์ ๋๋กญ(Frame Drop)์ ์ ๋ฐํฉ๋๋ค.
์ด๋ฌํ ํ๊ณ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ์ ๋ํฐ์ C# Job System๊ณผ Burst Compiler๋ฅผ ๋์
ํ์ฌ ์ฐ์ฐ์ ๋ฐ์ดํฐ ์งํฅ ๊ตฌ์กฐ(Data-Oriented Design)๋ก ์ ํํ๋ ๊ธฐ๋ฒ์ด ๋๋ฆฌ ํ์ฉ๋๊ณ ์์ต๋๋ค. ํ์ง๋ง ๋ฉํฐ์ค๋ ๋ฉ ํ๊ฒฝ์ผ๋ก ์ ํํ๋ ๊ณผ์ ์์ ๋น๊ด๋ฆฌํ ๋ฉ๋ชจ๋ฆฌ(Unmanaged Memory)์ธ NativeArray์ ์ฌ๋ฐ๋ฅด์ง ์์ ์๋ช
์ฃผ๊ธฐ ๊ด๋ฆฌ๋ก ์ธํด ๋ฉ๋ชจ๋ฆฌ ๋์(Memory Leak)๋ ๋์์ฑ ์ถฉ๋(Race Condition) ๋ฌธ์ ๊ฐ ๋น๋ฒํ๊ฒ ๋ฐ์ํฉ๋๋ค. ๋ณธ ๊ธ์์๋ ์ค์ ํ์
์ฐ๋ ๊ณผ์ ์์ ๋ฐ์ํ๋ ๊ธฐ์ ์ ๋ณ๋ชฉ์ ๋ถ์ํ๊ณ , ์ด๋ฅผ ์ต์ ํํ ํธ๋ฌ๋ธ์ํ
์ฌ๋ก๋ฅผ ๊น์ด ์๊ฒ ๋ค๋ฃน๋๋ค.
1. ๋ฉ์ธ ์ค๋ ๋ ๋ณ๋ชฉ ์์ธ๊ณผ ๋ฐ์ดํฐ ์งํฅ ํจ๋ฌ๋ค์ ์ ํ
๊ธฐ์กด์ ๊ฐ์ฒด ์งํฅ ๋ฐฉ์(Object-Oriented Programming, OOP)์ผ๋ก ๊ตฌํ๋ A* ๊ธธ์ฐพ๊ธฐ ์๊ณ ๋ฆฌ์ฆ์ ๋จ์ผ ํ๋ ์ ๋ด์์ ๋ฉ์ธ ์ค๋ ๋์ CPU ์์์ ๊ณผ๋ํ๊ฒ ์ ์ ํฉ๋๋ค. ์๋ฐฑ ๋ช ์ NPC๊ฐ ๋์์ ๊ธธ์ฐพ๊ธฐ๋ฅผ ์์ฒญํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์ค์ฒฉ๋์ด ๋ฐ์ํฉ๋๋ค.
- ๊ฐ๋น์ง ์ปฌ๋ ์ (Garbage Collection, GC) ๋ถํ: ๋งค ํ๋ ์ ๋ ธ๋ ํ์์ ์ํด ์์ฑ๋๋ C# ํด๋์ค ๊ฐ์ฒด(Node Object)๋ค์ด ํ ๋ฉ๋ชจ๋ฆฌ(Heap Memory)์ ํ ๋น๋์ด ์ฃผ๊ธฐ์ ์ธ GC ์คํ์ดํฌ๋ฅผ ์ ๋ฐํฉ๋๋ค.
- L1/L2 ์บ์ ๋ฏธ์ค(Cache Miss): ์ฐธ์กฐ ํ์ (Reference Type) ๋ฐ์ดํฐ๋ค์ด ๋ฉ๋ชจ๋ฆฌ์์ ํํธํ๋์ด ๋ฐฐ์น๋๋ฏ๋ก, CPU ์บ์ ํจ์จ์ด ๊ธ๊ฒฉํ ๋จ์ด์ง๋๋ค.
- ์ฝ์ด ํ์ฉ ๋ถ๊ท ํ: ๋ค์ค ์ฝ์ด(Multi-Core) ํ๊ฒฝ์์๋ ๋ถ๊ตฌํ๊ณ ๋ฉ์ธ ์ค๋ ๋ ํ๋๋ง 100% ๊ฐ๋๋๊ณ ๋๋จธ์ง ์์ ์ค๋ ๋(Worker Thread)๋ ์ ํด ์ํ์ ๋น ์ง๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ๊ตฌ์กฐ์ฒด(Struct) ์ค์ฌ์ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ฐฐ์ด์ ๊ตฌ์ฑํ๊ณ , ์ฐ์ฐ ๋ก์ง์ C# Job System์ผ๋ก ๊ฒฉ๋ฆฌํ์ฌ ์์ ์ค๋ ๋(Worker Threads)๋ก ๋ถ์ฐ์์ผ์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ LLVM ๊ธฐ๋ฐ์ Burst Compiler๋ฅผ ๊ฒฐํฉํ๋ฉด CIL(Common Intermediate Language) ์ฝ๋๊ฐ ์ต์ ํ๋ ์ฌ๋(SIMD, Single Instruction Multiple Data) ์ด์ ๋ธ๋ฆฌ ์ฝ๋๋ก ๋ณํ๋์ด ์์ญ ๋ฐฐ ์ด์์ ์ฐ์ฐ์๋ ํฅ์์ ์ป์ ์ ์์ต๋๋ค.
2. C# Job System & Burst Compiler ๊ธฐ๋ฐ ๊ธธ์ฐพ๊ธฐ ๊ตฌ์กฐ ์ค๊ณ
๋๊ท๋ชจ ๊ธธ์ฐพ๊ธฐ๋ฅผ ๋ณ๋ ฌํํ๊ธฐ ์ํด์๋ ๋จผ์ ๊ทธ๋ฆฌ๋(Grid) ์ ๋ณด์ ์ด๋ ๊ฒฝ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ณ ์ฑ๋ฅ ์ปจํ
์ด๋์ธ NativeArray ๋ฐ NativeParallelHashMap์ผ๋ก ๋ณํํด์ผ ํฉ๋๋ค. Burst Compiler์ ์ ์ฝ ์ฌํญ์, ๊ด๋ฆฌํ ๊ฐ์ฒด(Managed Object)์ธ Class๋ Reference ํ์
์ Job ๋ด๋ถ์์ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ์ค์ง ๋ธ๋ฆฌํฐ๋ธ ํ์
(Blittable Types)๊ณผ ๊ตฌ์กฐ์ฒด๋ง ํ์ฉํด์ผ ํฉ๋๋ค.
A* ์๊ณ ๋ฆฌ์ฆ์ฉ Native Parallel Job ๊ตฌํ ์์
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
[BurstCompile(FloatMode = FloatMode.Fast, FloatPrecision = FloatPrecision.Standard, CompileSynchronously = true)]
public struct PathfindingJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> StartPositions;
[ReadOnly] public NativeArray<float3> TargetPositions;
[ReadOnly] public NativeArray<bool> GridObstacleMap;
public int GridWidth;
public int GridHeight;
// ๊ธธ์ฐพ๊ธฐ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํ ๊ธฐ๋ณธ ์ปจํ
์ด๋ (์ธ๋ฑ์ค๋ณ ์ฐธ์กฐ)
[NativeDisableParallelForRestriction]
public NativeArray<UnsafeList<float3>> CalculatedPaths;
public void Execute(int index)
{
float3 start = StartPositions[index];
float3 target = TargetPositions[index];
// A* Pathfinding ์๊ณ ๋ฆฌ์ฆ ์ฐ์ฐ ์ํ (Value Type ๊ธฐ๋ฐ ์์ฑ)
// [SIMD ์ต์ ํ ์ ์ฉ ๋์ ๊ตฌ๊ฐ]
UnsafeList<float3> path = CalculatedPaths[index];
path.Clear();
// ๋
ธ๋ ํ์ ๋ก์ง ์ฒ๋ฆฌ...
path.Add(start);
path.Add(target);
}
}
์ ์ฝ๋์์ [BurstCompile] ์์ฑ์ ์ปดํ์ผ๋ฌ์๊ฒ ํด๋น ๊ตฌ์กฐ์ฒด๋ฅผ ๊ณ ์ฑ๋ฅ ๊ธฐ๊ณ์ด๋ก ๋ณํํ๋ผ๋ ์ง์๋ฅผ ๋ด๋ฆฝ๋๋ค. FloatMode.Fast ์ต์
์ ํ์ฑํํ๋ฉด ๋ถ๋ ์์์ ์ฐ์ฐ์ ์๊ฒฉ์ฑ์ ์ฝ๊ฐ ์ํํ๋ ๋์ ๋ ์ง์คํฐ ํ์ฉ์ฑ์ ๊ทน๋ํํ์ฌ ์ฐ์ฐ ์๋๋ฅผ ๋ํญ ํฅ์์ํต๋๋ค.
3. NativeArray ๋ฉ๋ชจ๋ฆฌ ๋์์ ์์ ์ฑ ํธ๋ฌ๋ธ์ํ
C# Job System์์ ๊ฐ์ฅ ์น๋ช ์ ์ด๊ณ ๋น๋ฒํ๊ฒ ๋ฐ์ํ๋ ๋ฌธ์ ๋ ๋น๊ด๋ฆฌํ ๋ฉ๋ชจ๋ฆฌ์ ํด์ ๋๋ฝ์ผ๋ก ์ธํ ๋ฉ๋ชจ๋ฆฌ ๋์(Memory Leak)์ ๋๋ค. C#์ ๊ธฐ๋ณธ GC๋ Native Container(NativeArray, NativeList ๋ฑ)์ ๋ด๋ถ ๋ฒํผ๋ฅผ ์๋์ผ๋ก ํด์ ํด์ฃผ์ง ์์ผ๋ฏ๋ก, ๊ฐ๋ฐ์๊ฐ ์ง์ ์๋ช ์ฃผ๊ธฐ(Lifecycle)๋ฅผ ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
ํธ๋ฌ๋ธ์ํ 1: Allocator ์ ํ ๋ฏธ์ค๋ก ์ธํ ์ค๋ฒํค๋ ๋ฐ ๋์
Native Container ์์ฑ ์ ์ง์ ํ๋ Allocator ํ ๋น ์ ์ฑ
์ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์นฉ๋๋ค.
| ํ ๋น์ ์ข ๋ฅ (Allocator) | ์๋ช ๋ฒ์ (Lifespan) | ํน์ง ๋ฐ ๊ถ์ฅ ์ฉ๋ |
|---|---|---|
| Allocator.Temp | 1 ํ๋ ์ ์ด๋ด | ๊ฐ์ฅ ๋น ๋ฆ. Job์ ์ ๋ฌํ ์ ์์ผ๋ฉฐ ๋จ์ผ ๋ฉ์๋ ๋ด๋ถ์์๋ง ์ฌ์ฉ ํ ์ฆ์ ํด์ . |
| Allocator.TempJob | ์ต๋ 4 ํ๋ ์ | Job์ ์ ๋ฌํ๋ ๋น๋๊ธฐ ๋ฐ์ดํฐ์ฉ. ์ค๋ ๋ ์์ ํ๋ฉฐ ์์ ์๋ฃ ํ ๋ฐ๋์ ํด์ ํ์. |
| Allocator.Persistent | ์๊ตฌ์ (์๋ ํด์ ์ ๊น์ง) | ๊ธด ์๋ช ์ ๊ฐ์ง ๋งต ๋ฐ์ดํฐ, ๊ทธ๋ฆฌ๋ ์ ๋ณด ์ ์ฅ์ฉ. ํด์ ๋๋ฝ ์ ๋์ ๋ฐ์ ์ํ ๋์. |
์ค๋ฌด ์์ธ ๋ถ์: ๋งค ํ๋ ์ ๊ธธ์ฐพ๊ธฐ๋ฅผ ์ค์ผ์ค๋งํ๋ฉด์ Allocator.TempJob์ผ๋ก ์์ฑํ NativeArray๋ฅผ Job์ด ์ข
๋ฃ๋๊ธฐ ์ ์ Dispose()ํ์ง ์๊ฑฐ๋, ํ๋ ์์ด ์ง์ฐ๋์ด 4ํ๋ ์ ์ ํ์ ์ด๊ณผํ๋ฉด ์ ๋ํฐ์ ์์ ๊ด๋ฆฌ ์์คํ
(Safety System)์ด ๊ฒฝ๊ณ ๋ฅผ ์ถ๋ ฅํ๊ณ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ์ค๋ฉ๋๋ค.
ํธ๋ฌ๋ธ์ํ 2: JobHandle ์์กด์ฑ ์ฐ๊ฒฐ๊ณผ Deferred Dispose ํจํด
๋ฉ์ธ ์ค๋ ๋์์ Job์ ์๋ฃ๋ฅผ JobHandle.Complete()๋ก ์ฆ์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ ๋ฉ์ธ ์ค๋ ๋ ๋๊ธฐ ๋ณ๋ชฉ(Main Thread Stall)์ ์ผ์ผํค๋ฏ๋ก ๋น๋๊ธฐ ํ๋ฆ์ ๊นจ๋จ๋ฆฝ๋๋ค. ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ์ง ์๊ณ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์ ํ๊ฒ ํด์ ํ๋ ค๋ฉด ์์กด์ฑ ๊ธฐ๋ฐ ํด์ (Deferred Dispose) ํจํด์ ์ ์ฉํด์ผ ํฉ๋๋ค.
public class PathfindingManager : MonoBehaviour
{
private NativeArray<float3> startPositions;
private NativeArray<float3> targetPositions;
private JobHandle pathfindingJobHandle;
public void RequestPathfinding(Vector3[] starts, Vector3[] targets)
{
// ์ด์ ์์
์ด ์งํ ์ค์ด๋ผ๋ฉด ์๋ฃ ๋ณด์ฅ
pathfindingJobHandle.Complete();
int count = starts.Length;
// NativeArray ํ ๋น (TempJob ์ฌ์ฉ)
startPositions = new NativeArray<float3>(count, Allocator.TempJob);
targetPositions = new NativeArray<float3>(count, Allocator.TempJob);
// ๋ฐ์ดํฐ ๋ณต์ฌ
for (int i = 0; i < count; i++)
{
startPositions[i] = starts[i];
targetPositions[i] = targets[i];
}
PathfindingJob job = new PathfindingJob
{
StartPositions = startPositions,
TargetPositions = targetPositions
};
// Job ์ค์ผ์ค๋ง
pathfindingJobHandle = job.Schedule(count, 32);
// [์ค์ Solution] Job ์๋ฃ ํ ๋ฉ์ธ ์ค๋ ๋ ๋๊ธฐ ์์ด ์๋์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๊ฐ ํด์ ๋๋๋ก ์ฒด์ด๋
pathfindingJobHandle = startPositions.Dispose(pathfindingJobHandle);
pathfindingJobHandle = targetPositions.Dispose(pathfindingJobHandle);
}
private void OnDestroy()
{
// ์ฌ ์ ํ ๋๋ ๊ฐ์ฒด ํ๊ดด ์ ๋จ์์๋ ํธ๋ค ๊ฐ์ ์ข
๋ฃ ๋ฐ ๋์ ๋ฐฉ์ง
pathfindingJobHandle.Complete();
}
}
์ ์ฝ๋์ ๊ฐ์ด NativeArray.Dispose(JobHandle inputDeps) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด, ์ง์ ํ JobHandle์ ์ฐ์ฐ์ด ๋๋๋ ์ฆ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋ ์ ์ดํ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์์ ํ๊ฒ ํด์ ๋ฉ๋๋ค. ๋ฉ์ธ ์ค๋ ๋์์ Complete()๋ฅผ ๋๊ธฐ์ ์ผ๋ก ํธ์ถํ ํ์๊ฐ ์์ด์ง๋ฏ๋ก ๋ฉ์ธ ์ค๋ ๋ ๋ณ๋ชฉ์ ์์ ํ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
ํธ๋ฌ๋ธ์ํ 3: ์์ธ ์ํฉ(Edge Cases) ๋ฐ ๋๋ฉ์ธ ๋ฆฌ๋ก๋ ์ฒ๋ฆฌ
๊ฐ๋ฐ ๊ณผ์ ๋ฐ ์๋ํฐ ํ๊ฒฝ์์ ๋ฐ์ํ ์ ์๋ ์ฃผ์ ์ฃ์ง ์ผ์ด์ค์ ๊ทธ ๋์ฒ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์๋ํฐ ํ๋ ์ด ๋ชจ๋ ์ค๋จ ์ ๋์: ์ ๋ํฐ ์๋ํฐ์์ Play ๋ฒํผ์ ๋๋ฌ ์ ์งํ ๋, Execution Order์ ์ํด
OnDestroy()๊ฐ ์คํ๋์ง ์๊ณ ๋๋ฉ์ธ ๋ฆฌ๋ก๋(Domain Reload)๊ฐ ๋ฐ์ํ๋ฉด Persistent ํ ๋น์๊ฐ ๋์๋ฉ๋๋ค. ์ด๋ฅผ ์ํดAppDomain.CurrentDomain.DomainUnload์ด๋ฒคํธ์ ํด์ ๋ก์ง์ ๋ฑ๋กํ๋ ์์ธ ์ฒ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. - ๋น๋๊ธฐ ๊ธธ์ฐพ๊ธฐ ์ทจ์ ์์ฒญ: NPC๊ฐ ์ด๋ ์ค ์ฌ๋งํ๊ฑฐ๋ ํ๊ฒ์ด ํ๊ดด๋ ๊ฒฝ์ฐ, ์คํ ์ค์ธ JobHandle์ ์ฆ์ ์ค๋จํด์ผ ํฉ๋๋ค. ์ด๋
JobHandle.Complete()๋ฅผ ํธ์ถํ์ฌ ์์ ์ ๋๋ธ ๋ค ์์ ํ๊ฒ ๋ฐฐ์ด์ ๋น์์ผ ๋์ ์ก์ธ์ค ์๋ฌ(InvalidOperationException)๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค. - ์์ ๊ฒ์ฌ ์์คํ
ํ์ฉ: Project Settings -> Player -> Scripting Define Symbols์
ENABLE_UNITY_COLLECTIONS_CHECKSํ๋๊ทธ๋ฅผ ๋ช ์ํ์ฌ, ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐ์ ์ง์ ์ ์ฝ์คํ(Callstack)์ ํธ๋ํนํ๋๋ก ์ค์ ํฉ๋๋ค.
4. ์ฑ๋ฅ ์ธก์ ๋ฐ ํ๋กํ์ผ๋ง ๋น๊ต ๋ถ์
๋์ผํ ๋งต ํ๊ฒฝ์์ 2,000๊ฐ์ NPC๋ฅผ ๋์์ผ๋ก ๊ธฐ์กด NavMeshAgent ๋ฐฉ์๊ณผ Burst + C# Job System ๋ณ๋ ฌ ๊ธธ์ฐพ๊ธฐ ๋ฐฉ์์ ํ๋ ์ ์์ ์๊ฐ์ ์ ๋ํฐ ํ๋กํ์ผ๋ฌ(Unity Profiler)๋ก ๋น๊ต ๊ฒ์ฆํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
| ๊ตฌ๋ถ (2,000๊ฐ ๊ฐ์ฒด ์ฐ์ฐ) | ๋ฉ์ธ ์ค๋ ๋ CPU ์๊ฐ | GC Alloc (ํ๋ ์๋น) | ํ๊ท ํ๋ ์๋ ์ดํธ(FPS) |
|---|---|---|---|
| ๊ธฐ์กด ๋จ์ผ ์ค๋ ๋ (NavMesh/OOP) | 42.5 ms | 184.2 KB | ~18 FPS (์ฌ๊ฐํ ๋) |
| Job System + Burst Compiler | 1.8 ms | 0 B (Zero GC) | 140+ FPS (๋งค์ฐ ์ํ) |
๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ, C# Job System๊ณผ Burst Compiler๋ฅผ ๋์ ํจ์ผ๋ก์จ ๋ฉ์ธ ์ค๋ ๋ ์ฐ์ฐ ์๊ฐ์ด ์ฝ 95% ์ด์ ๊ฐ์ํ์์ผ๋ฉฐ, ๊ฐ๋น์ง ์ปฌ๋ ์ ํ ๋น(GC Alloc)์ 0 Bytes๋ก ์ ์ดํ์ฌ ํ๋ ์ ๋๋กญ ํ์์ ์๋ฒฝํ ํด๊ฒฐํ ์ ์์์ต๋๋ค.
๊ฒฐ๋ก ๋ฐ ํต์ฌ ์์ฝ
๋๊ท๋ชจ NPC ๊ธธ์ฐพ๊ธฐ ์ฐ์ฐ์์ ๋ณ๋ชฉ์ ์๋ฒฝํ ์ ๊ฑฐํ๊ธฐ ์ํด์๋ ์ฐ์ฐ ์์ฒด์ ์ํคํ ์ฒ๋ฅผ ๋ฐ์ดํฐ ์งํฅ์ ์ผ๋ก ๋ฆฌํฉํ ๋งํ๋ ์์ ์ด ํ์์ ์ ๋๋ค. ์ด ๊ณผ์ ์์ C# Job System๊ณผ Burst Compiler๋ ๊ฐ๋ ฅํ ์ฑ๋ฅ ํฅ์์ ์ ๊ณตํ์ง๋ง, ๋น๊ด๋ฆฌํ ๋ฉ๋ชจ๋ฆฌ์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์๊ฒฉํ๊ฒ ์ ์ดํด์ผ๋ง ์์ ์ฑ์ ๋ด๋ณดํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ๊ตฌ์กฐ ๋จ์ํ: Job ๋ด๋ถ๋ก ์ ๋ฌํ๋ ๋ชจ๋ ๋ฐ์ดํฐ๋
NativeContainer๋ฐ Value Type ๊ตฌ์กฐ์ฒด๋ก ๋ณํํด์ผ ํฉ๋๋ค. - ๋น๋๊ธฐ ํด์ ์ฒด์ด๋:
NativeArray.Dispose(JobHandle)์ ์ฌ์ฉํ์ฌ ๋ฉ์ธ ์ค๋ ๋์ ๋๊ธฐ ์๊ฐ์ ์ ๋ก(Zero)ํํ์ธ์. - ๋์ ์ถ์ ์์คํ ์ ์ง: ๊ฐ๋ฐ ๋จ๊ณ์์๋ ์ ๋ํฐ์ Collection Safety Check ๊ธฐ๋ฅ์ ํญ์ ํ์ฑํํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ์กฐ์นํด์ผ ํฉ๋๋ค.
๋ณธ ๊ฐ์ด๋์์ ์ ์ํ ํจํด๊ณผ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ํธ๋ฌ๋ธ์ํ ๊ธฐ๋ฒ์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ค๋ฉด, ๋๊ท๋ชจ ๋ฉํฐํ๋ ์ด์ด ๊ฒ์์ด๋ ๋ณต์กํ ์๋ฎฌ๋ ์ด์ ํ๊ฒฝ์์๋ ์ต์ ์ ํ๋ ์ ์ ์ง๋ฅผ ๋ฌ์ฑํ ์ ์์ ๊ฒ์ ๋๋ค.