Confused about some design decisions

I briefly checked out this whole Burst / DOTS thingy and I must say I am a bit confused.

As far as I understand (correct me if I’m wrong) the general sentiment is that moving to DOTS offers better performance at the cost of reduced ability to write clean, abstract code. This post for example:

The response to this post was:

HOWEVER, functional programming / data driven development evangelists have claimed for years that moving to these paradigms offers dramatic improvements in code quality, regardless of its performance. According to them it is object-oriented and imperative programming that leads to convoluted, overly complex and bug prone code, while functional and data driven programming enables us to write simple, abstract, easy to understand and bug resistant code. However, they admit that their focus on immutability might make code less performant in some cases.

So what is the truth? Does ditching OOP and switching to data driven programming offer (a) cleaner code at the cost of reduced performance or (b) better performance at the cost of worse code? Because I’ve heard both claims.


My guess is that if Unity’s approach to ECS leads to difficulties in writing code then this is because of Unity’s insistence to keep using C#.

As far as I understand the tenets of DOTS are the direct opposite of what C# was designed for. To reduce cache misses we want data driven code, for ease of parallelization we want immutability, since the garbage collector is one of the major performance bottlenecks we want manual memory management, and to reduce performance impacts of code interpretation and JIT activation during runtime we want ahead of time compilation to native code rather than IL bytecode. So we now want functional, data driven, AOT compiled code with manual memory management. C#, however, is, and has always been an imperative, object-oriented, bytecode interpreted language with automatic memory management. It looks like C# is one of the worst language choices for DOTS.

As far as I’m aware the major reason why Unity uses C# is to reduce entry barrier. Most developers are already familiar with OOP and C# is more straightforward than, say, Rust, which, despite its other merits, is regarded as one of the more difficult languages available. However, C# may only be easy to learn if it used to what it was designed for. The bastardization of C# to write functional, data driven code with manual memory management suddenly makes C# a far more difficult language and certainly less convenient than languages designed from scratch to support such paradigms.

Another big reason for continued support of C# are historical reasons. Unity was always developed with C# in mind, lots of already existing code is written in C#, game developers who use Unity are familiar with C#, so moving away from C# would be a very bad decision now.

I checked out the manual of Burst: https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html Actually, is it still C#? The supported subset of C# is so small that no C# code not written from scratch with Burst in mind can be hoped to be compiled by Burst. But probably the worst part is that in certain parts language semantics have been changed:

(the quotation is from https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/csharp-language-support.html)

Arguably this what Burst supports is no longer C#, but rather, it is an entirely different language inspired by C#.

Developers’ pre-existing expertise with C# might indeed be, in a small part, transferable to Burst, at the cost however of using a language that is ill-suited to the goals of Burst.

What are you asking exactly?

In my opinion it’s much easier to write gameplay code with ECS, than traditional OOP. Better architecture + better performance.

C# is a great language. I’m glad they did it in C#.

You might want to read this: Unity Blog

1 Like

Wow, this is a very interesting position. Apparently C# on Burst is better than Rust at these very goals Rust was designed for. BTW I am not ironic, if Unity indeed managed this then I guess this is a very major achievement.

1 Like

Most likely you are confused, because you are learned and trained your brain to use OOP paradigm and you probably didn’t spend much time with DOD/DOP paradigm.

DOTS requires rewiring your brain, to think about the system design differently.

It is not that hard at all, if programmer designer of the system, was writing the code with a performance in mind, using DOD instead OOP I’m many, if not most of solutions.

If you haven’t been using DOD and you spent many years with only OOP, then you may have potential difficulties, or challangies to switch paradigms. But not impossible. Rather may require more effort to do so.

For anyone who was using OOP and yet had performance in mind, I am pretty sure the system design was including many DOD elements. And shifting to DOTS world with ECS won’t be that paint full.

I yet would like to see reference claiming, that DOD or DOTS is reason for writing less cleaner code than OOP.

If anything, DOTS requires stipper learning curve.

So far I have seen people been happy with DOTS clean system design paradigm. Separation data from systems etc. Plus of cource the massive performance boost, in comparison to OOP.

It is worth to mention, that if planning applying some of DOTS in existing projects, it is safer redisgn smaller bits one by one, with jobs for example. Not all system need to be DOTS. And trying redesign everything may not be worth it.
Of course, not all games, or apss may gain from DOTS paradigm design.

Much easier is to start new project with DOTS in mind. Still however, it doesn’t have to be “pure” DOTS.

1 Like

It really doesn’t matter if Rust, or Unity C# is better at one thing.

What matters is, can you use relevant tool efficiently enough, to achieve design goals?

3 Likes

One thing strikes me in this post.

Quoting this blog post you cited:

In other words one of the things they don’t like in C++ is that C++ compilers (gcc? LLVM?) are black boxes to them: they all try to optimize the code but are very unpredictable at this.

Unity’s solution? Write their own compiler. Then they will be able to make sure their code compiles precisely as they want.

While this solution may work for Unity it is important to note that for developers who use Unity Burst is a blackbox as much as gcc and LLVM are blackboxes to Unity.

It’s not a black box though, they are giving us better tools to work with the compiler, also it has documentation. It’s specially tailored for game development. I recommend you watch some of their talks about Burst. I doubt you understand the point atm. The point is not to make everything open source

Unity has designed ECS in such a way that users do not really need to access pointers. I doubt they even considered Rust, which at the time was not very well established as a programming language.

You are not going to get a lot of support for switching to Rust. Many developers have large Burst code bases at this point; such a switch would be disastrous. Having complete control of the compiler also may help determinist floating points.

In the long, run 10 years plus, I could see myself switching to Bevy ECS with Rust, but that is an open-source project that has a long way to go and is currently not as compelling as Unity ECS. They are also working on determinism, but at this point, the System execution order, ECS queries and component iteration order in Bevy is nondeterministic. Bevy is open source, and from my interaction, with the developers working on making it deterministic, it seemed like it may take a while. They did say they have or plan to have an AVX2 option for floating point determinism not sure of exact status. They also explained to me their progress toward fixing nondeterministic behavior in their ECS, but my feeling is they are three plus years away from meaningful determinism. Rapier physics which works with Bevy has an ieee-754 determinism already.

In conclusion, between the two main options Bevy ECS with Rust and Unity ECS with Burst, I feel Unity ECS is the better option for the foreseeable future. Adopting either is a major decision and you would be well served to wait a few years if you can.

You misunderstood what they said. C++ compilers are a black box in the sense that they perform so much transformations/optimizations that it is quite difficult, not to say impossible, to predict how some code will behave, especially in regard to auto-vectorization. Burst gives you guaranteed and predictable vectorization, that the whole point of it.

More predictable defiantly not to the point of “guaranteed and predictable vectorization.” It depends on lots of stuff like Hints, [NoAlias] etc.

Rust has NoAlias rules for &mut. It is hard to say if auto vectorization in Rust is really worse. Rust will keep getting better and adding more features whereas Burst has only one vendor and is tied to C# baggage. Both Burst and Rust use LLVM.