An analytical performance comparison between DOTS and Monobehaviours, ease of use & utilization

An analytical performance comparison between DOTS and OOP, ease of use & utilization

Contents

1.     Introduction.

2.     The Inner Workings of DOTS & OOP.

3.     Performance Tests.

4.     Ease of use & utilization.

5.     Conclusion.

6.     Bibliography.

1.    Introduction

A large part of performance is how we handle and manipulate numerous objects within our game. Enter DOTS (Data Oriented Technology Stack), a new approach that promises to manage this challenge more effectively than the traditional method, known as OOP (Object Oriented Programming). Unfortunately, switching to DOTS isn’t a simple pivot; it’s more like starting over from square one. From the little experience I’ve had from it so far, I can already tell it feels like a completely different language, even though it’s still just C#.

I will be plunging myself into the depths of DOTS to understand it. I’ll scour the internet for every tidbit to get a grip on it. There are a couple of main questions I aim to answer: If DOTS truly is a game-changer (MY.GAMES, 2023),  how it fundamentally differs from OOP, the implementation differences (ease of use), utility & performance difference.

To showcase DOTS in action, I’ll program along with three of the demo’s Unity provides. The specific three focus on total game objects, changing of states on lots of objects, and lots of objects with physics calculation respectively. Besides having the DOD (Data Oriented Design)  version, I do also need it’s OOP counterpart which I will have to build from scratch, and try to mimic the DOD version of it as closely as possible for all three DOD versions. Once I have the six versions I will stress test them comparatively, which will illustrate the performance differences between a DOD and an OOP design. Each will be their own Unity Project, and optimized for its own design. I anticipate the OOP design will be especially tricky to get good performance with, since OOP isn’t made for a large sum of objects, unlike DOD.
(Will, Entities Tutorial: Tanks, 2023) (Will, Entities Tutorial: StateChange, 2023) (Will, Entities Tutorial: Kickball, 2023)

Documentation is crucial. As I navigate the complexities of DOTS, I’ll be compiling a report that details my observations, challenges, and findings. This report will not only serve as a reflection of my journey but also as a future reference for those looking to understand DOTS.

In essence, this project is about exploring the potential of DOTS, understanding its mechanics, comparing it to OOP, and documenting each step of the journey meticulously.

2.    The Inner Workings of DOTS & OOP

Unity’s Data-Oriented Technology Stack (DOTS) is a framework aimed at leveraging modern hardware for highly efficient and performant game development. Unlike traditional Object-Oriented Programming (OOP) that bundles data and functions together, DOTS separates them, focusing on a data-centric approach that leads to cache-friendly code. This dramatically speeds up game performance.

Lets take a simple example to illustrate the difference between an OOP and DOD structure, you’ve several spheres and a couple of colors on said spheres. We want to change the position of the balls that are green. In OOP, we loop through all of the sphere classes, and ask if it matches the color. This needs a lot of references, and these references could be scattered throughout the memory, which is not efficient for the CPU to pick up upon, since it requires more time to find where the attributes are cached. When we look at the DOD side of things, we can see that the attributes are neatly stacked into arrays since DOD doesn’t rely on references of the Sphere class to get the attributes it needs. It instead relies on separate components (Color and Position) that are packed into buffers, making them easily accessible in an efficient manner. (Unity Technologies, 2023)

The backbone of DOTS exists out of three things:

C# Job System: Enables you to distribute workloads across multiple CPU cores, offering parallel execution for tasks like AI calculations, physics, and more.

Entity Component System (ECS): A game object becomes an “Entity,” a simple container that holds “Components” (data). The “Systems” then read these components to perform game logic, allowing for easy parallelization.

Burst Compiler: A math-aware compiler that transforms C# code into highly optimized machine code, boosting performance significantly when used with DOTS.

By focusing on data and its transformation, DOTS enables the development of games that are incredibly fast and efficient, unlocking new possibilities for complex and interactive game worlds. Notably, memory management in DOTS is also much more manual as opposed to OOP, where pretty much everything is done automatically within the garbage collector, which could cause excess overhead.

If it wasn’t clear yet, there’s a major hierarchical difference between OOP and DOD, lets delve deeper into how both hierarchies are setup, from largest to smallest element:

DOD:

  1. World:
    • A container that holds a collection of entities, systems, and data. It’s the top-level organizational unit in DOTS.
  2. System:
    • A function or group of functions that operate on entities and components, executing game logic and transformations.
  3. Chunk:
    • A block of memory allocated for entities of the same archetype to store components in a contiguous memory layout for efficient processing.
  4. Archetype:
    • A template that defines a grouping of components. All entities with the same archetype have the same set of components.
  5. Entity:
    • A lightweight, unique identifier that represents an object or item in the game world. Entities themselves do not contain data or behavior.
  6. Component:
    • A blittable data structure that holds the state of part of an entity, such as position or health.

OOP:

  1. Scene:
    • The environment that holds and organizes the GameObjects, acting as a container for all objects present in the game level or part of the UI.
  2. GameObject:
    • The primary element that represents characters, items, or props in the game world, serving as containers for Components.
  3. Component (MonoBehaviour):
    • A class that encapsulates both data and behavior. MonoBehaviours can be attached to GameObjects to define their appearance, behavior, and interaction with the game world.
  4. Class:
    • The blueprint for creating Components, with fields to store data and methods to define behavior and functionalities.
  5. Inheritance:
    • A hierarchy where classes can inherit from other classes, allowing derived classes to extend or modify the behavior of base classes.
  6. Polymorphism:
    • The ability of GameObjects to use Components of different types interchangeably, where a GameObject can behave differently based on which specific MonoBehaviour it contains.

(Unity Technologies, 2023), (Unity Documentation, 2022) (Unity Documentation, 2022) (Unity Documentation, 2022) (Wagner, 2022) (Microsoft, 2023)

3.    Performance Tests

The performance tests were run on a computer with these specs.

From the very first demo that I followed, which then proceeded to remake in a normal OOP way, it was immediately clear just how much of a difference using DOTS can have. I had to turn down the rate of object creation on the OOP version so much, to make it even remotely run.

To give accurate measurements I decided to hard cap the game to exit after 30 seconds. So the profilers displayed have captured a full 30 seconds of gameplay, since the scene was loaded. Below are all the profiler runs of each version respectively:

Tanks DOTS/DOD scene view

Tanks DOTS/DOD performance view

Tanks MonoBehaviour/OOP scene view

Tanks MonoBehaviour/OOP performance view

StateChange DOTS/DOD scene view

StateChange DOTS/DOD performance view

StateChange MonoBehaviour/OOP scene view

StateChange MonoBehaviour/OOP performance view

Kickball DOTS/DOD scene view

Kickball DOTS/DOD performance view

Kickball MonoBehaviour/OOP scene view

Kickball MonoBehaviour/OOP performance view

After all of these profiler measurements I wanted to look into a way of comparing the DOTS and OOP versions directly to each other. I ended up stumbling upon an article (Bonet, 2020) detailing the Unity Profile Analyzer package. An extra package from Unity that you can download, to directly compare separate profiler runs. One thing that this package illustrates perfectly, is the difference in threading, we can see that the EditorLoop has the biggest overhead, far exceeding that of the maximum overhead of the DOTS version, which is only 2.58 ms instead of 19.10 ms for a single thread. From all the expiriments I have ran, I also managed to conclude that DOTS can handle 10 times the amount of objects as OOP, with relative easy, which is quite impressive. This truly just goes to show that DOTS’s power is just unparalleled when it comes to performance in comparison to OOP, if you’re willing to put in the effort to structure it in that DOD way.

If you want to look further into the numbers generated by the profiler, I’ve uploaded the profiler runs & comparisons to a Google Drive, here.

4.    Ease of use & utilization

So there’s quite a lot to be said about the ease of use and use cases from DOTS compared to regular OOP. Notably this section will be subjective to a certain extent, since it’s hard to quantify difficulty in ease of use, but there’s a relatively common consensus that DOD is harder to pick up/learn than OOP. There are several reasons why this is the case:

  1. Paradigm Shift: DOTS requires a different way of thinking about data and behavior, moving away from the object-oriented approach that most developers are familiar with.
  2. Concurrency Complexity: Understanding and effectively utilizing the multithreading potential of DOTS can be complex due to the need to manage data dependencies and race conditions.
  3. Manual Memory Management: DOTS requires a more hands-on approach to memory management, whereas OOP in Unity relies on garbage collection.
  4. Less Abstraction: DOTS often requires more detailed and granular control of the code, which can increase complexity. For example, in the first of my three demos, the DOTS version has three times the scripts then of that of the OOP version.
  5. ECS Learning Curve: The learning curve associated with DOTS stems from both mastering the ECS architectural model and becoming proficient with the new DOTS APIs. This encompasses understanding how data and behavior are handled differently compared to traditional OOP, as well as getting accustomed to the specific interfaces, functions, and performance-focused paradigms introduced by DOTS.
  6. Limited Resources and Documentation: As a newer system, there are fewer learning resources, tutorials, and community knowledge bases for DOTS compared to OOP.
  7. Tooling and Debugging: The tooling for debugging and profiling DOTS applications is still maturing, making it harder to diagnose and fix issues compared to OOP.
  8. Personal Experience: The programmer as an individual plays a big part in how well DOTS will be understood. If it’s a more inexperienced programmer, then they’ll have a harder time picking up DOTS, since DOTS in general is harder than OOP. Though there’s something to be said that even developers with primarily OOP-experience will still struggle, due to a lot of OOP-skills being non-transferable, in the way that Unity put it: “you need to ignore a lot of OOP concepts that might seem like absolutely fundamental aspects of programming. Forget encapsulation and data hiding. Forget inheritance and polymorphism. To a large extent, forget about reference types. These concepts will not help you.” (Unity Technologies, 2023)

As for utilization, the use cases are pretty clear. DOTS in Unity isn’t really far enough that you could make an entire project using just DOTS, there will always be at least a couple of MonoBehaviours, if not many. Hybrid is also just the better option since if there’s like a singular object that needs to have a specific behavior, throwing it through the whole DOTS system would be excessive and a waste of time, at such small scales performance won’t be noticeable. If there are big masses of objects, stuff that needs a lot of numeration, that’s where you’d want to use DOTS, think of games where you’d have massive interactive armies, or maybe just a heap load of physics calculation for a simulation game, and much more. It can be used in many projects, the decision whether to use DOTS or not should arise when you’re making a large-scale system. However this decision needs to be made at the start, because you cannot easily convert an OOP way to the DOTS way, nor the other way around. In the case of prototyping OOP would be superior than DOTS, since you can get something on paper way quick than having to dance around the hoops of DOTS, having to abide by it’s many (intended) restrictions.

5.    Conclusion

The analytical journey into the performance, ease of use, and utility of Unity’s DOTS compared to traditional MonoBehaviour OOP highlights a significant paradigm shift. DOTS presents a compelling case for high-performance game development, particularly in scenarios demanding extensive object manipulation. The performance tests unequivocally suggest DOTS’s superiority in handling large-scale simulations efficiently. However, the steep learning curve, lack of extensive resources, and the initial complexity of DOTS compared to the more intuitive MonoBehaviour approach suggest a trade-off between upfront development ease and runtime efficiency. This exploration underscores the importance of choosing the right approach based on project scale, complexity, and developer expertise, with DOTS being the go-to for heavy-duty tasks and MonoBehaviour remaining a reliable staple for less intensive applications and quicker prototyping needs.

6.    Bibliography

Bonet, R. T. (2020, March 11). Unity Profiler: How to Become Numbers-Driven With Unity Profile Analyzer. Retrieved from thegamedev.guru: https://thegamedev.guru/unity-performance/unity-profile-analyzer/

Microsoft. (2023, January 31). Polymorphism. Retrieved from learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/polymorphism

MY.GAMES. (2023, Juli 18). Exploring Unity DOTS and ECS: is it a game changer? Retrieved from medium.com: https://medium.com/my-games-company/exploring-unity-dots-and-ecs-is-it-a-game-changer-b21b5d18674b

Unity Documentation. (2022). Introduction to components. Retrieved from docs.unity3d.com: https://docs.unity3d.com/Manual/Hierarchy.html

Unity Documentation. (2022). Introduction to components. Retrieved from docs.unity3d.com: https://docs.unity3d.com/Manual/Components.html

Unity Documentation. (2022). MonoBehaviour. Retrieved from docs.unity3d.com: https://docs.unity3d.com/Manual/class-MonoBehaviour.html

Unity Technologies. (2023, October 21). EntityComponentSystemSamples. Retrieved from github.com: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/EntitiesSamples/Assets/README.md#entities-api-overview

Unity Technologies. (2023, June 6). Part 1: Understand data-oriented design. Retrieved from learn.unity.com: https://learn.unity.com/tutorial/part-1-understand-data-oriented-design?uv=2022.3&courseId=60132919edbc2a56f9d439c3#639aed80edbc2a6721c462b7

Wagner, B. (2022, Februari 16). Inheritance – derive types to create more specialized behavior. Retrieved from learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/inheritance

Will, B. (2023, June 1). Entities Tutorial: Kickball. Retrieved from github.com: https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/EntitiesSamples/Assets/Tutorials/Kickball

Will, B. (2023, June 1). Entities Tutorial: StateChange. Retrieved from github.com: https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/EntitiesSamples/Assets/Miscellaneous/StateChange

Will, B. (2023, June 1). Entities Tutorial: Tanks. Retrieved from github.com: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/EntitiesSamples/Assets/Tutorials/Tanks/README.md

Leave a Reply

Your email address will not be published. Required fields are marked *