Job System – Part 2

This is the second part of my series of articles about Job System, you can read my first article here:

https://mauroubaldo.com/job-system-part-1/

Now let’s talk about Native Containers with Jobs.

For Start this article I would like a quote from Unity’s official documentation, about what is Native Containers.

“A Native Container is a managed value type that provides a safe C# wrapper for native memory. It contains a pointer to an unmanaged allocation. When used with the Unity C# Job System, a Native Container allows a job to access data shared with the main thread rather than working with a copy.” Unity Documentation.

For more Infos, you can see the official documentation here: https://docs.unity3d.com/Manual/JobSystemNativeContainer.html

Now that we know what are Native Containers, we can talk about the types of native containers.

What are the types?

According to Unity documentation, the types are:

NativeArray – This is responsible for exposing a buffer of native memory for managed code.

NativeList – This is a resizable native array.

NativeHashMap – These are keys and value pairs.

NativeMultiHashMap – These are multiple values per key.

NativeQueue – This is a queue.

For more Infos you can see the official documentation mentioned in this part of this article:

NativeArray: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html

NativeCollections: https://docs.unity3d.com/Manual/JobSystemNativeContainer.html

For this article, I will only show the native array, because the native array does not need a preview package, but if you want to see other collections, you can search for Entity Component System (ECS) package.

How does it work?

Firstly you need to import the namespace Unity.Collections, at the moment that I am writing this article the Unity Engine only has available the NativeArray but in Entity Component System (ECS) package as mentioned, exist other Collections.

For this Article, I will use NativeArray.

Creating a NativeArray

For to create a NativeArray you only need to make this:

NativeArray<int> myNativeArray;

How to initialize a Native Array?

You can initialize a native array thus:

myNativeArray = new NativeArray<int>(arrayLenght, Allocator.Persistent);

Or using some constructor, NativeArray has three constructors, they are:

        public NativeArray(T[] array, Allocator allocator);
        public NativeArray(NativeArray<T> array, Allocator allocator);
        public NativeArray(int length, Allocator allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory);

Repair that We need to define an allocator, and Now I will explain what is this.

An Allocator is an Enumerator used to specify the type of allocation for our Native Array.

Using a native collection we need to use a memory allocator type.

An allocator can have three types:

Persistent: this is a very slow allocation, but your data may be available all the necessary time.

Temp: is a very fast allocation, but your lifetime is 1 frame or minus.

TempJob: is more fast than persistent but more slowly than temp, and your lifetime is 4 frames or minus.

For more info, you can check the official documentation here: https://docs.unity3d.com/Manual/JobSystemNativeContainer.html

In this article, I only will use a Persistent allocator, because is a very simple example, but I suggest that you test other allocators, as an exercise for improving your knowledge.

Now, that We know how to create a native array, let’s talk about how to work with a NativeArray.

The way to work with native arrays is the same as in other fields, but with a particularity, with the native array, you can copy values and paste them into other variables, after the end of the Job.

For example:

using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using Unity.Jobs;

public class JobExample2 : MonoBehaviour
{

    NativeArray<int> myFirstNativeArray;
    NativeArray<int> mySecondNativeArray;
    NativeArray<int> myResultNativeArray;
    int[] myFirstArray;
    int[] mySecondArray;
    public int[] myResultArray;

    MyJobWithArray myJobWithArray;
    JobHandle myJobWithArrayHandle;

    struct MyJobWithArray : IJob
    {

        public NativeArray<int> myJobNativeArray;
        public NativeArray<int> myJobNativeArray2;
        public NativeArray<int> myJobResultNativeArray;

        public void Execute()
        {
            for (int i = 0; i < myJobNativeArray.Length; i++)
                myJobResultNativeArray[i] = myJobNativeArray[i] + myJobNativeArray2[i];
        }
    }

    void Awake()
    {

        myFirstArray = new int[] { 1, 2, 3, 4};
        mySecondArray = new int[] { 5, 6, 7, 8 };
        myResultArray = new int[4];

        myFirstNativeArray = new NativeArray<int>(myFirstArray, Allocator.Persistent);
        mySecondNativeArray = new NativeArray<int>(mySecondArray, Allocator.Persistent);
        myResultNativeArray = new NativeArray<int>(myResultArray, Allocator.Persistent);

        myJobWithArray = new MyJobWithArray
        {
            myJobNativeArray = myFirstNativeArray,
            myJobNativeArray2 = mySecondNativeArray,
            myJobResultNativeArray = myResultNativeArray
        };

        myJobWithArrayHandle = myJobWithArray.Schedule();
    }


    void Start()
    {
        myJobWithArrayHandle.Complete();
        myJobWithArray.myJobResultNativeArray.CopyTo(myResultArray);

        for (int i = 0; i < myResultArray.Length; i++)
            Debug.LogError(myResultArray[i]);

        myFirstNativeArray.Dispose();
        mySecondNativeArray.Dispose();
        myResultNativeArray.Dispose();
    }

}

So here you can see how to copy the data of a Job for your code without value loss.

For the last is important to see that, I am using the method Dispose, this is to deallocate the memory allocated for use of the array.

Follow me on Instagram to stay known when I create new posts.

You can also leave your feedback, and suggestion, I will read them all, and I will answer for you.

Thanks for reading.

Job System – Part 1

For this article, I will make an introduction to the Unity Job System.

Firstly what is the Job System, and how does it work?

Job System basically is a Multi-threading system made by Unity to work with Threads in simple way inside of the games.

You can see more Infos here: https://docs.unity3d.com/Manual/JobSystem.html

Unity’s Job System has as its objective to generate more performance in your game, being able to generate great results, Unity Job System is better used with Burst Compiler, but you can use Job System separately.

For this Article, I will talk only about Job System.

So now Let’s go to talk about Unity Job System.

Today, the Job System has two ways of use, the first way (and the way used in this article) is the native way, to write this article, I am using Unity 2020.3.30 and He is available in this version (probably He also is available in other versions), another way, is using a package, that now (while I write this article) is in preview.

The Job System today only supports operations involving reference types, natively the value types don’t are supported (At the moment that I am writing this article).

To start, I need to talk that, Job System makes the use of Data Structures work, so all your functionalities used with the Job System, need to be inside a Struct that makes use of some Job Interface.

All data used in the job system is temporary. The Job System receives the values and manipulates, but your values cannot be copied for other variables outside of the Jobs, for making this, (Today) the unique way available is using NativeContainer, but this is a topic I talk about in another article.

How does this work?

Right after this, I can talk better about the Unity Job System.

To start you need to import the Namespace:

Unity.Jobs

or

UnityEngine.Jobs

What is the difference between them?

Unity.Jobs will permit that you make all operations with Job System (Except operations involving the Unity’s Transform component), and UnityEngine.Jobs will permit that you make the operations with transform.

After importing the namespace you need to create a data struct, this data struct needs to make use of some interface of JobSystem.

The interfaces of JobSystem are:

  • IJob (Unity.Jobs)
  • IJobFor (Unity.Jobs)
  • IJobParallelFor (Unity.Jobs)
  • IJobParallelForTransform (UnityEngine.Jobs)

For this article I will make a simple Job, with the IJob interface, all the Jobs Interfaces will implement a method named Execute.

Execute method is the main method of JobSystem, so your implementation needs to be made in him.

So here is our data struct:

struct UnityJobArticle : IJob
{
    public void Execute()
    {
        throw new System.NotImplementedException();
    }
}

Inside of the method Execute We will implement our action.

For example:

struct UnityJobArticle : IJob
{
    public int a;
    public int b;
    public int sum;

    public void Execute()
    {
        sum = a + b;
    }
}

Now, We will take about how to call a job.

To call a job we will need to do this process

Firstly We will create a variable of this struct as:

UnityJobArticle UnityJobArticleStruct;

After this, we will instantiate this variable in a method, for example:

void Start()
{
    UnityJobArticleStruct = new UnityJobArticle
    {
        a = 10,
        b = 10
    };
}

Repair that for this Case, I initialized a and b, but no sum, because for this case I will not need to give a value for sum because sum will receive the result of a plus b inside of the job, therefore your value is not important at this moment.

Now let’s talk about running the Job.

We have two ways to run the job, the first way is using a method named Run, this method makes your job be executed immediately.

Here is an example using the method Run:

void Start()
{
    UnityJobArticleStruct = new UnityJobArticle
    {
        a = 10,
        b = 10
    };

    UnityJobArticleStruct.Run();
}

More Infos about the method Run is available in Unity’s Documentation: https://docs.unity3d.com/2020.3/Documentation/ScriptReference/Unity.Jobs.IJobExtensions.Run.html

Our next way is using Job Scheduling.

What is Job Scheduling?

According to Unity’s documentation, the Job Scheduling will make that the Job is put in the Job Queue and run at an appropriate time.

For more Infos, you can see the Unity Documentation here: https://docs.unity3d.com/2020.3/Documentation/Manual/JobSystemSchedulingJobs.html

To make a Job Scheduling, we will need of a field of JobHandle.

What is the JobHandle?

The JobHandle is a struct used in JobSystem, She is returned when We schedule a Job, with it We can make some operations with your Job.

You can see more Infos in Unity Official Documentation: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html

Scheduling a Job

For scheduling a job, firstly, We need of a field of type JobHandle, and after this, We will call the method Schedule, available in Job System.

So here is an example:

void Start()
{
    JobHandle UnityJobArticleHandle;
    UnityJobArticleStruct = new UnityJobArticle
    {
        a = 10,
        b = 10
    };

    UnityJobArticleHandle = UnityJobArticleStruct.Schedule();
}

When you make a Job Schedule you can call JobHandle.Complete, to force that your job will be completed, According to Unity’s documentation, when you call the JobHandle.Complete, the Job and your dependencies will priority on the execution.

Here is an example:

void Awake()
{
    UnityJobArticleStruct = new UnityJobArticle
    {
        a = 10,
        b = 10
    };

    UnityJobArticleHandle = UnityJobArticleStruct.Schedule();
    UnityJobArticleHandle.Complete();
}

For more Infos about the JobHandle.Complete you can see the documentation here: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.Complete.html

For Last here is an example source code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;

public class JobExample : MonoBehaviour
{

    UnityJobArticle UnityJobArticleStruct;


    struct UnityJobArticle : IJob
    {

        public int a;
        public int b;
        public int sum;

        public void Execute()
        {
            sum = a + b;
            Debug.Log(sum);
        }
    }

    
    void Start()
    {

        JobHandle UnityJobArticleHandle;
        UnityJobArticleStruct = new UnityJobArticle
        {
            a = 10,
            b = 10
        };

        //UnityJobArticleStruct.Run();
        UnityJobArticleHandle = UnityJobArticleStruct.Schedule();
        UnityJobArticleHandle.Complete();

    }
    
}

For this article is this, in this article You understood the basics of the Job System, for the next articles of this series, I will continue talking about the Job System.

Follow me on Instagram to stay known when I create new posts.

You can also leave your feedback, and suggestion, I will read them all, and I will answer for you.

Thanks for reading.