Search Unity

Looking for a way to get started writing safe multithreaded code? Learn the principles behind our Job System and how it works together with the Entity Component System (ECS) and the Burst compiler from this brief intro!

In Unity 2017.3 we exposed our Job System to C# code. Together with our new Burst compiler and Entity Component System (ECS), the job system makes up a high-performance multithreaded system, that will make it possible for games to fully utilize the multicore processors available today.

The purpose of the Job System is to allow the game simulation to use all the available CPU cores. Almost all modern CPUs have multiple cores and the trend is increasing. Yet many games and applications rely on using just a single core. When you split your processing into multiple smaller chunks and run them across multiple cores, you are able to process simultaneously in parallel, instead of one after another. This uses the capacity of the cores more efficiently and therefore brings massive performance improvements. Or to be more specific, using all available cores makes the simulation use less wall-time (time on the clock from starting the simulation until completing it), without optimizing the thread-time (the number of CPU instructions spent computing the result).

Reducing wall-time

The easiest way to get started reducing wall-time with the Job System is to use ParallelFor jobs. A ParallelFor job is used when processing a large set of values in the same way. Essentially, the job system processes each item in the array individually using a job – which means all the items can be processed in parallel to each other utilizing multiple CPU cores, if available. In practice, the number of jobs is actually much lower than one per item in the array, there is one job per CPU core and they each get an even amount of items to process. Since some workers finish their work faster than others, we use something called work-stealing to even out the time spent on each core. When a worker has finished all its work, it looks at the other workers’ queues and tries to process some of the items assigned to another worker.

Going beyond ParallelFor

If you have some very heavy systems containing many similar items, ParallelFor works great. But even if you only have a few things of each type, you can take advantage of the Job System. On a high level, the design of a Job System is to split the entire application into small self-contained units of work called jobs. Each CPU core has its own thread executing these jobs, which makes all jobs run in parallel to each other. So as long as the different items don’t depend on each other, all you have to do is schedule jobs for them without waiting for any other jobs, and they will run in parallel to other things.

Schedule early, complete late

Something we often suggest when talking about the Job System is the concept of scheduling early and waiting late. The purpose of this pattern is to make sure the main thread doesn’t have to wait for the job to complete. By the time the main thread needs the results of a job, it should ideally already have finished executing. A very common question which does not have a simple answer is: Which update pass is “early” and “late”? What we mean when we say schedule early and wait late is that you should give the job as much time as possible to run. It doesn’t matter much in which part of the frame you schedule and wait, as long as they’re as far apart as possible. If one frame latency is acceptable, you can even wait for the job in the next frame. Any time you see a “wait” on the main thread in the profiler, you should investigate what it’s waiting for and, if you can, schedule that job earlier or complete it later to get rid of the wait.

What problem does it not solve?

A Job System is not designed to be a solution for long running low priority tasks, and it is not designed for operations waiting instead of using CPU resources, like IO. It’s still possible to do these things, but it’s not the primary purpose of the Job System, which means they come with some limitations you need to be aware of.

Cooperative multi-tasking

Each worker thread in the JobSystem is tied to a physical or a virtual CPU core. Once one of these threads start executing a job, the job will run to completion without any interruptions. If you want to share a CPU core with something else, you need to manually yield, and the only way to do that is to split your job into two jobs with dependencies between them. Since the system is never doing any context switching for you, a running job will occupy one full core of the CPU, even if you aren’t actually doing anything important.

How it works together with ECS and Burst

There are many implications for using the C# Job System, and generally speaking, this approach should lead to better performance across the board. This is particularly true as new Unity features like the Entity Component System and the Burst compiler technology come into play. The Entity Component System focuses on reducing the thread-time required to compute a result by organizing your data in a very cache-friendly way. Burst focuses on reducing the thread-time by optimizing your code better when it’s running within the job system. The goal of all these systems is to increase what is fundamentally possible in Unity in terms of performance, while still supporting existing workflows and making the transition easier.

Conclusion

Modern hardware architecture is equipped with and trending towards having multiple cores. Yet many processes rely on using just a single core. By running multiple processes across multiple cores, you’re able to run it simultaneously in parallel, instead of one after another, thus utilizing the capacity of the cores more efficiently and gaining massive performance improvements.

The new C# Job System takes advantage of multiple cores in a safe and easy way. Easy, as it’s designed to open this approach up to your scripts and allow you to write fast jobified code, and safe because it provides protection from some of the pitfalls of multi-threading, such as race conditions.

You can use the new multithreaded systems to create games that run on a variety of hardware. You can also take full advantage of the performance gains to create richer game worlds with more units and more complex simulations.

To learn more and grab resources to get started please visit https://unity3d.com/unity/features/job-system-ECS.

Got questions? Talk to us on the ECS and C# Job System forum.

17 Comments

Subscribe to comments

Leave a reply

You may use these HTML tags and attributes: <a href=""> <b> <code> <pre>

  1. The useful article! Thank you.

  2. It is available for mobile?

    1. Yes you can use job system and ecs on mobile

    2. Yes, the JobSystem is available on all platforms Unity supports with the exception of the .NET scripting backend. If you run it on a platform which does not support threads all jobs will execute on the main thread when you call Complete on their job handles.

  3. Thx Tim for the article and Joachim Ante writing the code! There will be some live training videos about Entity Component System?

      1. There is unfortunately not a lot of training videos available yet. We are still actively developing ECS and the API is still not final. The number of training videos and tutorials should increase a lot as the technology matures and comes out of preview. The main resource maintained by the team developing ECS is the manual – https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/index.md – and presentations from Unite

  4. How is this different than the .NET TPL (Task Parallel Library) that has been a pet of the .NET framework since version 4? Is this implementation optimized for the games domain in some way?

    1. Some of the main advantages we see with the Unity JobSystem over TPL are
      * Less thread switches, Unity uses a JobSystem for native code inside the engine. TPL will use separate threads which will compete with the engine for CPU resource, the Unity JobSystem uses the same threads which means less contention and less thread switches
      * Safety, the Unity JobSystem detects race conditions which is very hard to do manually even if you are experienced with multi-threaded code
      * No gc-allocactions, the JobSystem uses native memory and manual memory management to avoid all gc allocations which reduces gc stalls
      * Better code-gen, by implementing the JobSystem ourselves we have more control over how everything works, which allows us to compile the job code with Burst and generate code which is an order of magnitude faster than mono

  5. I’ve not really worked with the job system yet, but unfortunately i learned nothing valuable here. The sub header had me expect the article to go atleast a little deeper and not just talk about how nice this system is..

  6. So what’s new? Unity is really weird company rehashing stuff over and over without giving any new info. Aren’t there anything exciting going on? How about some info on the new network stack or Kinematica that you promised for the end of summer? I’m going to be really disappointed if the new FPS example is nothing more than just a couple of players running around and shooting without any provisions for scaling up to 100s of players. It uses ECS so I assume it will perform quite well for large number of players from the beginning. And there isn’t any info on Kinematica nor any forum thread. Michael promised something for the summer and the release before the end of the year yet there is no info and no Unite session on it. Is Michael still with Unity? Unless he is, I’m not sure what’s keeping it so silent. How about blogs on those two topics for your supporters? Thanks.

  7. The Job System is always spoken about in conjunction with ECS and without examples it is hard to tell, can the Job System be used without ECS to do multi threading in the standard workflow?

    1. You can. I haven’t used ECS at all, but I’ve played around with Jobs to multithread a few effects. Totally doable.

    2. You can use it I is helpful for transforms, because it is only way to modify them in non-main thread

    3. The JobSystem is an engine feature without any dependency on other packages, so it can be used by itself.
      Burst can be used to make the jobs written for the JobSystem run faster, it is an optional feature on top of the JobSystem.
      ECS is a new way of writing data-oriented code. ECS uses the JobSystem to make the code multi-threaded, and it makes it easier to write multi-threaded code since it gives you more control over your data.

      All three are designed to fit well together, but you can use the JobSystem without ECS as long as you store your data in NativeContainers manually so it can be passed to the jobs.

  8. That was a painfully dry read. And absolutely no examples or analogies given to properly describe anything.

    1. Head over to the forums: https://forum.unity.com/forums/entity-component-system-and-c-job-system.147. That’s where the action is. Examples and resources are there.