Overview

12 Unity DOTS

The chapter explains how data-oriented design sets the stage for Unity DOTS by organizing gameplay information into flat arrays that are cache-friendly and easy to process efficiently. It then builds on that foundation by showing how Burst and the Jobs system can increase performance through two different forms of concurrency: SIMD, which performs the same operation on multiple values at once on a single core, and parallel processing, which spreads work across multiple CPU cores. The main idea is that the same array-based architecture that improved cache usage also makes it much easier to adopt DOTS tools, though doing so introduces additional code and tighter Unity-specific constraints.

Burst is presented as the first major optimization step, requiring the use of native memory types such as NativeArray and Burst-compatible math types like float2. The chapter walks through converting gameplay arrays, allocating and disposing of native memory manually, and rewriting enemy movement so it can be compiled with Burst. The result is the same gameplay logic, but executed more efficiently, with the chapter noting a substantial speedup in practice. It also emphasizes that Burst’s benefits come with tradeoffs, including extra complexity and reduced portability outside Unity.

The chapter then shows how Jobs can parallelize the same kind of array-based logic across worker threads, using IJobParallelFor and careful annotations such as [ReadOnly] and [NativeDisableParallelForRestriction]. It also discusses ECS as Unity’s formal DOD implementation, but argues that for this project, ECS would add unnecessary overhead because the game already benefits from direct array-based data handling. Finally, it demonstrates how TransformAccessArray can parallelize GameObject transform updates without fully converting the project to ECS, while still warning that scheduling and synchronization costs mean these tools should be used only when they address a real performance bottleneck.

Without SIMD, when doing work on array data, we can only work on 1 element at a time. With SIMD, we can process multiple elements at once. If our CPU has 128-bit registers, we can work on 4 integer or floating-point values at the same time.
By combining Burst and Jobs, we can pass chunks of our array data to each CPU core and have them work on them in parallel.
We can use Unity’s built-in physics with our DOD architecture. Our physics objects, be them GameObjects or entities, can directly call our Game singleton, which in turn can call static functions in Logic to modify our GameData accordingly. Then we can update the GameObjects/Entities in Board.Tick() based on the GameData changes.
The m_enemyToPoolIndex array acts as a bridge between the alive enemies and the pool objects. If the first alive enemy index is 0, then the pool index is 1, and that’s the pool object we need to update. The second alive enemy index is 2, which points to pool index 3, so we update that next. The third alive enemy index is 3, which points to pool index 0. The enemy at index 1 is a dead enemy, so it is not referenced in the AliveEnemyIndices array. The pool object at index 2 is also unused, so nothing references it.
Every pool object has a corresponding entry in the TransformAccessArray. We need to run over all the elements in the array, so for each one we check if we have a valid pool index. The first one points to enemy index 1, so we use that enemy’s position to update the first transform in the access array. The second one points to -1, so we know it points to a dead enemy and we don’t have to update it. The third points to the enemy at index 0. Etc.
The Job runs over all the transforms in the TransformAccessArray, so we have to use PoolToEnemyIndex to determine which enemy they belong to, and if they are alive, and we should update the position.

Summary

  • The Burst compiler allows us to use SIMD instructions to improve performance by processing multiple data elements in a single instruction.
  • The Burst compiler requires us to use NativeArray and DOTS-compatible types (e.g., float2)
  • The Jobs system enables multi-core parallelism by splitting array-based work across multiple threads.
  • The Jobs system introduces scheduling overhead that can negatively affect performance when working with small amounts of data.
  • ECS stands for Entity Component System, a design pattern often used with data-oriented design.
  • Entity is the index into the array data.
  • Component is the arrays of data.
  • System is the logic that operates on the data.
  • ECS increases code complexity and is not always necessary for performance gains.
  • TransformAccessArray allows parallel updates of GameObject transforms without using ECS.
  • TransformAccessArray requires iterating over all elements of the array, which may introduce extra branching if some GameObjects are unused.
  • TransformAccessArray should only be used if updating GameObject transforms is the bottleneck.

FAQ

What is the main performance benefit of Unity DOTS in this chapter?Unity DOTS improves performance by combining SIMD-friendly code execution and parallel processing across multiple CPU cores, which lets the game do more work at the same time.
How does SIMD differ from parallel processing?SIMD runs the same operation on multiple values at once on a single core, while parallel processing splits work into tasks that run across multiple CPU cores.
Why does Burst require NativeArray instead of regular C# arrays?Burst needs native arrays so it can manage memory in a thread-safe and deterministic way. Regular C# arrays use managed memory, which Burst cannot safely access.
Why do we need to use float2 instead of Vector2 with Burst?float2 is compatible with Unity.Mathematics and Burst, while Vector2 is part of UnityEngine and is not the preferred type for Burst-compatible code.
Why must NativeArray objects be manually disposed?NativeArray memory is not garbage-collected, so it must be explicitly released with Dispose() when it is no longer needed.
What does the [BurstCompile] attribute do?[BurstCompile] tells Unity to compile the class or method with the Burst compiler so it can use optimized low-level code, including SIMD instructions.
Why are the input arrays in MoveEnemiesJob marked with [ReadOnly]?[ReadOnly] tells Unity that those arrays will only be read, not modified, which helps the job system safely distribute work across threads.
Why is [NativeDisableParallelForRestriction] needed for EnemyPosition in the job?It tells Unity to allow parallel writes to EnemyPosition using enemyIndex instead of the loop index, because the job updates positions at different array locations.
Why does the chapter say ECS adds code complexity?ECS requires separating data into components and logic into systems for every piece of functionality, which creates extra overhead and more code to manage.
When should TransformAccessArray be used?Use TransformAccessArray when updating GameObject transforms is a performance bottleneck, since it allows transform updates to run in parallel through Jobs, but it adds scheduling overhead.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free