Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Understanding Jobs

Discussion in 'Entity Component System' started by equal, Dec 31, 2021.

  1. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Hi,

    i have trouble understanding the Unity Jobs system.
    Lets say i have a Job and run it:
    Code (CSharp):
    1.     var myJobHandle = myJob.Schedule(); //<-- Heavy Job, at least 10 seconds of work
    2.     myJobHandle.Complete();
    3.  
    From my understanding (classic precedural Programming) there is literaly 0 time between Line 1 and Line 2.
    Because i could just write:
    Code (CSharp):
    1.     var x1 = 1;
    2.     var x2 = 2;
    3.  
    In the second example, the CPU stack would execute those 1 after another (im not 100% sure, not that good in Cpu stack stuff). But it should go something like this: allocate memory, set memory to 1, push memory away, allocate another memory, set memory to 2. Done.

    What im saying there is nothing in between the 2 Lines in the second example. But there seems to be something between the 2 Lines in the first example. BUT we are still in the same Frame... does time propagate in subFrame space here?

    That seems to be the only explanation i have, that quite a lot happens in Example 1 before line 2.
    Now comes the tricky part, does this freeze the execution of other things that run on the Main Thread?
    I would bet that when i put an infinite loop into a job and command it to ".Complete" it would not, so does it hold the Main Thread hostage until actuall complete?

    Also, this is my main Question: If we wait for whatever Job to complete, does the System/Cpu/Unity suspend anything here until job completion? What is it that gets susspended (Method Block, wholeScript.cs)? How on earth does the System/Cpu/Unity find back the susspended thing or checks on its state?

    as always, i welcome all contributions.
     
    Last edited: Jan 1, 2022
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,984
    You are correct in that there is virtually no time between lines 1 and 2 even in your first snippet. Rarely would you ever want to do that. The only exception being a parallel job with a large workload that you want to complete as fast as possible. When the main thread waits for a job to complete, it begins to act like a worker thread, able to process other jobs on the queue, or if the job queue is starved, it goes idle.

    Often you want to schedule a job (and ensure it gets scheduled using JobHandle.ScheduleBatchedJobs), do some other things on the main thread, possibly even returning execution back to the engine, and then later on call Complete().
     
  3. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Thanks for your awnser. I noticed that i missed somethign, It was my assumtion that the Job in the first Example is a lot of work, at least some seconds, here 10. But i didnt mention it anywhere. I edited the Post to reflect that.

    Now there must be some Magic happening between line 1 and 2 of Example 1, right?
     
  4. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    327
    Think of .Complete() as a "wait here until job is done". It is basically a really fancy way of doing
    Code (CSharp):
    1. while(!myJobHandle.IsCompleted)
    2. {
    3.     Thread.Yield();
    4. }
    Depending on the state of the jobsystem and the job you are executing a few things can happen:
    • by far most common: .Schedule() put your job in the queue of jobs to be executed. Then one of the Job Threads (which you can see in the profiler timeline view) will take it to be executed. As the time between your line 1 and line 2 is normally close to zero the complete call will just loop until the jobhandle is completed. If you have a job taking 10s the .Complete() call will "stall" for these 10 seconds. As this will most of the time not be what you want during gameplay (No rendering or other mainthread activity for these 10s) you need to be careful with immediately calling .Complete().
    • The you got lucky case: You just executed the queueing of the job (line 1 completed) and now the OS decides it is time to let an Idle Job Thread run which immediately finishes with your job before giving command back to the main thread where the while loop will immediately see .IsCompleted is true. I am referring to this as the "you got lucky case" because in case of a 10s job this will all but guaranteed not happen. It does implicitly demonstrate something you will need to at least know about when understanding how jobs work internally: Threads and how an OS handles them.
    • .Schedule() decides to execute your job on the main thread in which case Schedule already executes the job code and therefore everything will look like you are used to in procedural programming.
     
    Catsoft-Studios likes this.
  5. benzy54n

    benzy54n

    Joined:
    Dec 21, 2013
    Posts:
    34
    A few questions from this:

    1) Is there a way to prevent the main thread from doing a task? So if for instance I wanted the main thread to queue up all the jobs I need and then use the main thread to monitor the completion of tasks in real time with say a progress bar.

    2) One thing I never quite grasped was if I didn't use the .Complete() call the job would get executed eventually right? Right now I am always calling the .Complete() in my main thread after scheduling. Its rather clunky but my initial thought is that its faster than as I don't know when that job will get picked up and executed if I don't do the .Complete(). Would scheduling all the jobs and then waiting naturally for them to get executed be take longer?
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,627
    Main thread never do work on a job unless it's blocked until job is complete (i.e. something is calling Complete())
     
    benzy54n likes this.
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,984
    Calling Complete() prioritizes the job. JobHandle.ScheduleBatchedJobs() will allow the jobs to be scheduled eventually. Some other methods also seem to flush jobs to the job queue. I know JobHandle.CombineDependencies() does this in 2020.3.
     
  8. tassarho

    tassarho

    Joined:
    Aug 25, 2019
    Posts:
    64
    the alternative to "Complete()" is something a never found a solution to, and i try various method nothing works.
    Is there something similar to an async await? or the couroutine? i tried to put the IsComplete in both of those but with no succes..

    CASE:
    Let says i schedule a job but dont necessarily need it to be complete this frame, BUT i need to know when the job is complete to start an event or do something else, how do i get this information? does someone has the solution to this and if possible an exemple?
     
  9. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    Well, a simple example is here, BUT it's very dangerous to do, as this job will completely block one worker thread for its own, and the job system has a limited worker thread pool. Usually, you want to have not more than 1 long-running job (better not to have even that one), also you should be careful with containers allocation for these jobs.
    TLDR - not use them, or use them extremely carefully and rare.
    upload_2022-1-8_2-25-14.png upload_2022-1-8_2-25-22.png upload_2022-1-8_2-25-44.png
     
    bb8_1 likes this.
  10. tassarho

    tassarho

    Joined:
    Aug 25, 2019
    Posts:
    64
    thanks for the solution, so you advise, whenever i need a job to be complete to launch an event or something else, even if i don't need necessarily the job to finish the frame i scheduled it, i should prefer jobhandle.Complete()?