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.

20 replies on “What is a Job System?”

I was fiddling around with making a Reaction Diffusion simulator and looked into Jobs to see if I could get Unity to run the operation in a threaded manner and I found two potential issues:
1) Most importantly, there was no way to say «run this task forever.» That is, I couldn’t continuously ask a task to do its Thing over and over again the way RD (or other cellular automata) work: writing their output back into the input in some fashion (could be the same array in the same place, the same array in a different place, or a separate array) and then repeating for the next iteration.
2) There was no way to tell Jobs that they needed to be mindful of where the other Jobs were in the process. For example a RD simulation needs to have each worker offset by 3 rows so that no worker writes to a location that the other workers are reading from.

At a minimum I needed (1) in order to use Jobs to run the simulation and I couldn’t just create a new Job every frame, as the simulation takes ~150ms to complete when single-threaded (with 12 threads managed through standard multithreading I can get it down to about 35ms–there’s some overhead keeping each thread from advancing past the point where its upstream neighbor is). (2) is only needed if each thread writes back to the same location that the data is being read from, but if ParallelFor is involved and data is written to a secondary location, then a secondary ParallelFor is needed to copy from the secondary back to the primary before the first ParallelFor is run again, triggering issue 1 again.

Hej Tim,

Oerhört fascinerande läsning, tack!

Jag skulle vilja komma i kontakt med dig gällande en företagsmässa på Lunds Tekniska Högskola. Var når jag dig som bäst?

[IN CASE OF ENGLISH]
Hi Tim,

Interesting read, thank you!

I would like to discuss a business fair at Lund University. Where would you like to initiate this conversation?

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

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?

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..

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.

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?

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

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

Comments are closed.