Best Practice (Please Read)

In the following sections, we briefly go through a few techniques that can help you understand tricks when using JobSchedulers.

Multi-threaded or single-threaded Julia session

It is recommended to use JobSchedulers in multi-threaded Julia sessions.

Jobs are controlled using a main scheduler task (JobSchedulers.SCHEDULER_TASK[]). This task always binds to thread ID (tid) 1 and does not migrate to other threads. During initiation, JobSchedulers.jl checks available tids in the default thread pool. If the default thread pool is empty after excluding tid 1, JobSchedulers.jl will use a single-thread mode (JobSchedulers.SINGLE_THREAD_MODE[]). Otherwise, JobSchedulers.jl will use a multi-thread mode.

Single-thread Mode

The maximum number of CPU is default to the system CPU (Sys.CPU_THREADS).

All Jobs are migratable, and they might yield to other tasks.

Multi-thread Mode

The maximum number of CPU is default to

  • number of threads in the default thread pool, if you use any interactive threads. (ie. starting julia with -t 10,1.)

  • number of threads in the default thread pool minus 1, if you do not use interactive threads. (ie. starting julia with -t 10.)

The tids that JobScheduler.jl can use are stored in a Channel JobSchedulers.THREAD_POOL[].

If you submit a job assigning ncpu > 0,

  • the job does not migrate to other threads.

  • Also, if you only use JobSchedulers to schedule tasks, your tasks will not be blocked by other tasks at any time. It is important when your tasks need quick response (like a web API server). Therefore, you can ignore the existance of interactive threads when using JobSchedulers.jl.

    Info

    JobSchedulers.jl even solves the issue of interactive tasks prior to the official Julia introducing task migration (partially solved) and the interactive/default thread pools.

If you set ncpu = 0 to your job,

  • the job is migratable and does not take any tid from JobSchedulers.THREAD_POOL[].

    Tip

    Use ncpu = 0 only when a job is very small, or a job that spawns and waits for other jobs:

    using JobSchedulers
    
    small_job = Job(ncpu = 0) do
        # within the small job,
        # submit 100 big jobs
        big_jobs = map(1:100) do _
            @submit ncpu=1 sum(rand(9999999))
        end
        # post-process of big jobs
        total = 0.0
        for j in big_jobs
            total += fetch(j)
        end
        total
    end
    submit!(small_job)
    total = fetch(small_job)
    # 4.999998913757924e8

Avoid simultaneous use of Job and other multi-threaded methods using the :default thread pool

Since a normal Job binds to a tid in the default thread pool and does not migrate, it is better not to simultaneously use Job and other threaded methods, such as Threads.@spawn and Threads.@threads.

Also, JobScheduers.jl has very low extra computational costs (scheduling 10,000 jobs within 0.01 second), so normal threaded methods can be replaced with Job.

If you really want to use both Job and other threaded methods, it is better to make sure to run them at different time. You may use wait_queue(), scheduler_stop(), and scheduler_start() in this situation.