Search Unity

Some notes on the job system

Discussion in 'C# Job System' started by sschoener, Apr 27, 2019.

  1. sschoener

    sschoener

    Joined:
    Aug 18, 2014
    Posts:
    73
    Hi there,
    I compiled some notes about the job system here and thought that they might be useful to someone else. It is meant as an addition to the existing documentation (it frequently links to it) and covers the different job types, native containers, and special attributes that you might not immediately find out about when starting off with the system.
    I hope this is somewhat helpful :) Please let me know if you have any feedback.
     
    optimise, eizenhorn and jdtec like this.
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    First of all it’s using derpecated naming.
    Second:
    Code (CSharp):
    1. IJobParallelFor (documentation) - a job that uniformly applies a kernel to each element in an array,
    it’s little bit unclear, coz it process not one element per thread but how many you sets in batch count on schedule.
    This wrong too:
    Code (CSharp):
    1. IJobParallelForTransform (documentation) - a job that uniformly applies a kernel to each transform in an array
    This job split data by root transform per thread, which mean if you have all your transforms in one root, they’ll be processed on same thread.
    This wrong:
    Code (CSharp):
    1. Note that concurrent writing in case of the job system is still only allowed within a single job (but that job might be split across multiple threads).
    With correct jobs chain you can use same container in multiple jobs in same system.
    You can write in any element, not depend on index if you sure in safety and not write to same element:
    Code (CSharp):
    1. These will ensure that for any array your job is using, you are only writing to the current index (or an index within the batch range for the batched version).
    I think there is can be more mistakes, but I’m in bed and haven’t more time to inspect :)
     
  3. sschoener

    sschoener

    Joined:
    Aug 18, 2014
    Posts:
    73
    @eizenhorn:
    Thanks for the input :)

    What naming is deprecated, specifically? I am working with the most recent releases and would love to see what changed.

    I did not intend to say that IJobParallelFor explicitly runs each kernel in its own thread (that would be madness in almost all cases). I'm merely saying that it applies the kernel for each index and that is true. I guess it comes down to what you call a kernel and it seems that we have different notions of what the kernel is. (I'm not so keen on talking about that kind of job because (I think) there is no confusion around it; I mostly include it for completeness.) I'll probably reword that so it is clearer.

    The point about the transform array is an excellent addition, I wasn't aware of that particular detail. Again, I'm not trying to say that it does each call in a separate thread.

    My point about concurrent writing using the concurrent version of a queue can be clearly seen in when you run this code:
    Code (CSharp):
    1. public class TestBehaviour : MonoBehaviour
    2. {
    3.     struct ConcurrentTestJob : IJob
    4.     {
    5.         [WriteOnly]
    6.         public NativeQueue<int>.Concurrent Queue;
    7.  
    8.         public void Execute()
    9.         {
    10.         }
    11.     }
    12.  
    13.     private void NoConcurrentWriting()
    14.     {
    15.         var queue = new NativeQueue<int>(Allocator.TempJob);
    16.         var concurrent = queue.ToConcurrent();
    17.  
    18.         var job1 = new ConcurrentTestJob() {
    19.             Queue = concurrent
    20.         };
    21.         var job2 = new ConcurrentTestJob() {
    22.             Queue = concurrent
    23.         };
    24.         JobHandle.CombineDependencies(
    25.             job1.Schedule(),
    26.             job2.Schedule()
    27.         ).Complete();
    28.  
    29.         queue.Dispose();
    30.     }
    31.  
    32.     private void Update() {
    33.         NoConcurrentWriting();
    34.     }
    35. }
    It will throw this:
    I'm calling that out because the name "Concurrent" might mislead you to think that this is possible, because all the two jobs will be doing is potentially writing concurrently to a thing that should support concurrent writing.


    Also, the parallel jobs very much enforce that you do not write to foreign indices. Try this code to see the runtime error that I am referring to:
    Code (CSharp):
    1. public class TestBehaviour : MonoBehaviour
    2. {
    3.     struct ParallelTestJob : IJobParallelFor
    4.     {
    5.         [WriteOnly]
    6.         public NativeArray<int> Data;
    7.  
    8.         public void Execute(int index)
    9.         {
    10.             if (index > 0)
    11.                 Data[0] = index;
    12.         }
    13.     }
    14.  
    15.     private void NoWritingForeignIndices()
    16.     {
    17.         var data = new NativeArray<int>(2, Allocator.TempJob);
    18.         new ParallelTestJob() {
    19.             Data = data
    20.         }.Schedule(data.Length, 1).Complete();
    21.         data.Dispose();
    22.     }
    23.  
    24.     private void Update() {
    25.         NoWritingForeignIndices();
    26.     }
    27. }
    This throws an exception, even though it is evident that there is only a single write happening in total:
    I can see that my statement is not entirely accurate because you only get the error when you are writing to an index outside of the indices assigned to your batch, but the error message is pretty clear about the intended behavior (I assume that the fact that this does not throw for batch-sizes > 1 in this scenario is a bug).
     
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Most recent release is preview 30
    https://forum.unity.com/threads/upcoming-api-changes-in-0-27.656245/
    Yep missleading naming :) kernels for me is like cores/threads :) Just rememeber old good compute shader where you set kernels count per dispatch :) More preferable word here is execution :)
    As I told
    I use concurrent containers in many chained jobs without Complete calling. It’s was discussed many times, last you can read, for example, here :)
    https://forum.unity.com/threads/lock-free-native-queue.660286/
    I mean this is only “synthetic” error :) just for exclude some basic race conditions, in most cases we know what we do and using NativeDisableParallelFor attribute, and we write to array elemens safely.
    I’m know all thes errors and went through them more than year ago, I’m no need run code to remember them :D from first ECS and Job System releases, but with experience you understand it better, some points in you arguments is good I agree, but many people reading that can be....confused in future (yeah I think it’s good words :) ) when they suddenly find that some things they can do in legit avoiding of some restrictions :) I just recommend you add more notes to your notes :) Anyway good job and it’ll be interesting for newcomes in ECS, when you rename some api to actual:p
     
  5. jdtec

    jdtec

    Joined:
    Oct 25, 2017
    Posts:
    302
    This was a good quick and handy read, thanks for posting. I've been using DOTs for a few months now but hadn't noticed ResizableArray64 and hadn't really read into BurstDiscard until now.
     
    sschoener likes this.
  6. sschoener

    sschoener

    Joined:
    Aug 18, 2014
    Posts:
    73
    I'm very well aware that this is the latest release. I am, however, not referring to the ECS in the blog post at all, it's only about jobs. I am not aware of anything in the notes that uses deprecated features and/or terminology. If you can point me to specific terms/functions/types that are deprecated without being marked as such in code, I'll happilly change them :)

    I have spent some years working with CUDA and my usage of the term kernel mostly originates from there and now, to me, refers mainly to the program you specify to be run by the coprocessor (independent of the actual scheduling that the system is performing); but I agree that it might be less confusing to just go with function.

    Thanks for the link, I'm always up for some lock-free programming :) I think I clearly understand what the job safety system is doing, and without any attributes it will definitely stop you from attempting to write concurrently to some other operation on the same container. That's the entire raison d'etre of the safety system in the first place ;) Chaining jobs through specifying dependencies does not allow you to write concurrently to a container -- quite the opposite: It serializes the jobs so that any potential writes do not happen concurrent to another write/read at the same location. This is also why the restriction for parallel-for jobs is in place.

    Again, this kind of error is exactly what the system was built for, to my understanding. Of course you can in principle do anything because you can use raw pointers etc. but that's missing the point of the system. If you just add attributes to ignore all safety checks everywhere, then you might as well disable all the checks to begin with.

    Thanks! The main reason for these notes is the lack of documentation for some of the more interesting job types like the filtering jobs.
     
    eizenhorn likes this.
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Here is my bad :D As I told I'm was in bed, and not read clearly! Sorry so for that, it was really my blindness! I'm apologize for confusing!
    Yep they in chain, and dependency system prevent race conditions, but what I meant, may be I'm not explain clearly, It's that we can use one concurrent version in chain without any locks and main thread synch between jobs in chain, in other words - we schedule chain and this chain works on worker threads without any potential block points like one solid background work, it's my point :)
     
    sschoener likes this.
  8. sschoener

    sschoener

    Joined:
    Aug 18, 2014
    Posts:
    73
    Haha, I think we both actually agree on the main talking points :) Thanks again for the input, it's appreciated! I have reworded a few sections. Your game looks super-awesome, by the way :)
     
  9. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    PanMadzior, GilCat and sschoener like this.