BluFedora Job System v1.0.0
This is a C++ job system library for use in game engines.
|
Namespaces | |
namespace | detail |
Classes | |
struct | InitializationLock |
struct | InitializationToken |
struct | JobSystemContext |
struct | JobSystemCreateOptions |
The runtime configuration for the Job System. More... | |
struct | JobSystemMemoryRequirements |
The memory requirements for a given configuration JobSystemCreateOptions . More... | |
class | LockedQueue |
class | MPMCQueue |
struct | Splitter |
class | SPMCDeque |
class | SPSCQueue |
struct | Task |
struct | TaskData |
A buffer for user-data you can write to, maybe large enough to store task data inline. More... | |
union | TaskFnStorage |
union | TaskMemoryBlock |
struct | TaskPool |
struct | TaskPtr |
struct | ThreadLocalState |
Typedefs | |
using | WorkerID = std::uint16_t |
The id type of each worker thread. More... | |
using | TaskFn = void(*)(Task *) |
The signature of the type of function for a single Task. More... | |
using | TaskHandle = std::uint16_t |
using | TaskHandleType = TaskHandle |
using | AtomicTaskHandleType = std::atomic< TaskHandle > |
using | WorkerIDType = WorkerID |
using | AtomicInt32 = std::atomic_int32_t |
using | Byte = unsigned char |
using | AtomicTaskPtr = std::atomic< TaskPtr > |
Enumerations | |
enum class | QueueType : std::uint8_t { NORMAL = 0 , MAIN = 1 , WORKER = 2 } |
Determines which threads the task will be allowed to run on. More... | |
enum class | SPMCDequeStatus { SUCCESS , FAILED_RACE , FAILED_SIZE } |
Functions | |
std::size_t | NumSystemThreads () noexcept |
Makes some system calls to grab the number threads / processors on the device. This function can be called by any thread concurrently. More... | |
InitializationToken | Initialize (const JobSystemMemoryRequirements &memory_requirements={}, void *const memory=nullptr) noexcept |
Sets up the Job system and creates all the worker threads. The thread that calls 'Job::Initialize' is considered the main thread. More... | |
void | SetupUserThread () |
Must be called in the callstack of the thread to be setup. More... | |
const char * | ProcessorArchitectureName () noexcept |
An implementation defined name for the CPU architecture of the device. This function can be called by any thread concurrently. More... | |
std::uint16_t | NumWorkers () noexcept |
Returns the number of workers created by the system. This function can be called by any thread concurrently. More... | |
WorkerID | CurrentWorker () noexcept |
The current id of the current thread. This function can be called by any thread concurrently. More... | |
bool | IsMainThread () noexcept |
Allows for querying if we are currently executing in the main thread. More... | |
void | Shutdown () noexcept |
This will deallocate any memory used by the system and shutdown any threads created by 'bfJob::initialize'. More... | |
Task * | TaskMake (const TaskFn function, Task *const parent=nullptr) noexcept |
Creates a new Task that should be later submitted by calling 'TaskSubmit'. More... | |
TaskData | TaskGetData (Task *const task, const std::size_t alignment) noexcept |
Returns you the user-data buffer you way write to get data into your TaskFn. More... | |
void | TaskAddContinuation (Task *const self, Task *const continuation, const QueueType queue=QueueType::NORMAL) noexcept |
A 'continuation' is a task that will be added to a queue after the 'self' Task has finished running. More... | |
template<typename T > | |
T * | TaskDataAs (Task *const task) noexcept |
Grabs the user-data pointer as the T you specified. No safety is guaranteed, this is just a dumb cast. More... | |
template<typename T , typename... Args> | |
void | TaskEmplaceData (Task *const task, Args &&... args) |
Calls the constructor of T on the user-data buffer. More... | |
template<typename T > | |
void | TaskSetData (Task *const task, const T &data) |
Copies 'data' into the user-data buffer by calling the T copy constructor. More... | |
template<typename T > | |
void | TaskDestructData (Task *const task) |
Helper for calling destructor on the task's user data. More... | |
template<typename Closure > | |
Task * | TaskMake (Closure &&function, Task *const parent=nullptr) |
Creates a new task making a copy of the closure. More... | |
void | TaskIncRef (Task *const task) noexcept |
Increments the task's ref count preventing it from being garbage collected. More... | |
void | TaskDecRef (Task *const task) noexcept |
Decrements the task's ref count allow it to be garbage collected. More... | |
bool | TaskIsDone (const Task *const task) noexcept |
Returns the done status of the task. More... | |
template<typename ConditionFn > | |
void | TickMainQueue (ConditionFn &&condition) noexcept |
Runs tasks from the main queue as long as there are tasks available and condition returns true. More... | |
void | TickMainQueue () noexcept |
Runs tasks from the main queue until it is empty. More... | |
void | TaskSubmit (Task *const self, const QueueType queue=QueueType::NORMAL) noexcept |
Submits the task to the specified queue. More... | |
void | WaitOnTask (const Task *const task) noexcept |
Waits until the specified task is done executing. This function will block but do work while being blocked so there is no wasted time. More... | |
void | TaskSubmitAndWait (Task *const self, const QueueType queue=QueueType::NORMAL) noexcept |
Same as calling taskSubmit followed by waitOnTask . More... | |
void | PauseProcessor () noexcept |
CPU pause instruction to indicate when you are in a spin wait loop. More... | |
void | YieldTimeSlice () noexcept |
Asks the OS to yield this threads execution to another thread on the current cpu core. More... | |
template<typename F , typename S > | |
Task * | ParallelFor (const std::size_t start, const std::size_t count, S &&splitter, F &&fn, Task *parent=nullptr) |
Parallel for algorithm, splits the work up recursively splitting based on the splitter passed in. More... | |
template<typename Splitter , typename Reducer > | |
Task * | ParallelReduce (const std::size_t start, const std::size_t count, Splitter &&splitter, Reducer &&reduce, Task *parent=nullptr) |
template<typename T , typename F , typename S > | |
Task * | ParallelFor (T *const data, const std::size_t count, S &&splitter, F &&fn, Task *parent=nullptr) |
Parallel for algorithm, splits the work up recursively splitting based on the splitter passed in. This version is a helper for array data. More... | |
template<typename... F> | |
Task * | ParallelInvoke (Task *const parent, F &&... fns) |
Invokes each passed in function object in parallel. More... | |
Variables | |
static constexpr std::size_t | k_FalseSharingPadSize = std::hardware_destructive_interference_size |
static constexpr std::size_t | k_CachelineSize = 64u |
static constexpr std::size_t | k_ExpectedTaskSize = std::max(std::size_t(128u), k_CachelineSize) |
static constexpr QueueType | k_InvalidQueueType = QueueType(int(QueueType::WORKER) + 1) |
static constexpr TaskHandle | NullTaskHandle = std::numeric_limits<TaskHandle>::max() |
struct Job::InitializationLock |
Definition at line 192 of file job_system.cpp.
Class Members | ||
---|---|---|
mutex | init_mutex | |
condition_variable | init_cv | |
atomic_uint32_t | num_workers_ready |
struct Job::JobSystemContext |
Definition at line 199 of file job_system.cpp.
Class Members | ||
---|---|---|
ThreadLocalState * | workers | |
uint32_t | num_workers | |
uint32_t | num_owned_workers | |
atomic_uint32_t | num_user_threads_setup | |
uint32_t | num_tasks_per_worker | |
InitializationLock | init_lock | |
const char * | sys_arch_str | |
size_t | system_alloc_size | |
size_t | system_alloc_alignment | |
bool | needs_delete | |
atomic_bool | is_running | |
LockedQueue< TaskPtr > | main_queue | |
mutex | worker_sleep_mutex | |
condition_variable | worker_sleep_cv | |
atomic_uint32_t | num_available_jobs |
struct Job::JobSystemCreateOptions |
The runtime configuration for the Job System.
Definition at line 85 of file job_api.hpp.
Class Members | ||
---|---|---|
uint8_t | num_user_threads | The number of threads not owned by this system but wants access to the Job API (The thread must call Job::SetupUserThread). |
uint8_t | num_threads | Use 0 to indicate using the number of cores available on the system. |
uint16_t | main_queue_size |
Number of tasks in the job system's QueueType::MAIN queue. (Must be power of two) |
uint16_t | normal_queue_size |
Number of tasks in each worker's QueueType::NORMAL queue. (Must be power of two) |
uint16_t | worker_queue_size |
Number of tasks in each worker's QueueType::WORKER queue. (Must be power of two) |
uint64_t | job_steal_rng_seed | The RNG for work queue stealing will be seeded with this value. |
struct Job::TaskData |
A buffer for user-data you can write to, maybe large enough to store task data inline.
If you store non trivial data remember to manually call it's destructor at the end of the task function.
If you call 'TaskEmplaceData' or 'TaskSetData' and need to update the data once more be sure to destruct the previous contents correctly if the data stored in the buffer is non trivial.
Definition at line 203 of file job_api.hpp.
Class Members | ||
---|---|---|
void * | ptr | The start of the buffer you may write to. |
size_t | size | The size of the buffer. |
union Job::TaskMemoryBlock |
Definition at line 167 of file job_system.cpp.
Class Members | ||
---|---|---|
TaskMemoryBlock * | next | |
unsigned char | storage[sizeof(Task)] |
struct Job::TaskPool |
Definition at line 174 of file job_system.cpp.
Class Members | ||
---|---|---|
TaskMemoryBlock * | memory | |
TaskMemoryBlock * | freelist |
struct Job::ThreadLocalState |
Definition at line 180 of file job_system.cpp.
Class Members | ||
---|---|---|
SPMCDeque< TaskPtr > | normal_queue | |
SPMCDeque< TaskPtr > | worker_queue | |
TaskPool | task_allocator | |
TaskHandle * | allocated_tasks | |
TaskHandleType | num_allocated_tasks | |
ThreadLocalState * | last_stolen_worker | |
pcg_state_setseq_64 | rng_state | |
thread | thread_id |
using Job::WorkerID = typedef std::uint16_t |
The id type of each worker thread.
Definition at line 54 of file job_api.hpp.
using Job::TaskFn = typedef void (*)(Task*) |
The signature of the type of function for a single Task.
Definition at line 55 of file job_api.hpp.
using Job::TaskHandle = typedef std::uint16_t |
Definition at line 82 of file job_system.cpp.
using Job::TaskHandleType = typedef TaskHandle |
Definition at line 83 of file job_system.cpp.
using Job::AtomicTaskHandleType = typedef std::atomic<TaskHandle> |
Definition at line 84 of file job_system.cpp.
using Job::WorkerIDType = typedef WorkerID |
Definition at line 85 of file job_system.cpp.
using Job::AtomicInt32 = typedef std::atomic_int32_t |
Definition at line 86 of file job_system.cpp.
using Job::Byte = typedef unsigned char |
Definition at line 87 of file job_system.cpp.
using Job::AtomicTaskPtr = typedef std::atomic<TaskPtr> |
Definition at line 115 of file job_system.cpp.
|
strong |
Determines which threads the task will be allowed to run on.
Definition at line 45 of file job_api.hpp.
|
strong |
Enumerator | |
---|---|
SUCCESS | Returned from Push, Pop and Steal. |
FAILED_RACE | Returned from Pop and Steal. |
FAILED_SIZE | Returned from Push, Pop and Steal. |
Definition at line 231 of file job_queue.hpp.
|
noexcept |
Makes some system calls to grab the number threads / processors on the device. This function can be called by any thread concurrently.
Can be called even if the job system has not been initialized.
Definition at line 866 of file job_system.cpp.
|
noexcept |
Sets up the Job system and creates all the worker threads. The thread that calls 'Job::Initialize' is considered the main thread.
memory_requirements | The customization parameters to initialize the system with. To be gotten from Job::MemRequirementsForConfig . |
memory | Must be memory_requirements.byte_size in size and with alignment memory_requirements.alignment . If nullptr then the system heap will be used. |
InitializationToken
can be used by other subsystem to verify that the Job System has been initialized. Definition at line 741 of file job_system.cpp.
References g_CurrentWorker, g_JobSystem, Job::JobSystemContext::init_lock, Initialize(), Job::JobSystemCreateOptions::job_steal_rng_seed, JobAssert, Job::JobSystemContext::main_queue, Job::JobSystemCreateOptions::main_queue_size, Job::JobSystemContext::needs_delete, Job::JobSystemCreateOptions::normal_queue_size, Job::JobSystemContext::num_available_jobs, Job::JobSystemContext::num_owned_workers, Job::JobSystemContext::num_tasks_per_worker, Job::JobSystemCreateOptions::num_user_threads, Job::JobSystemContext::num_user_threads_setup, Job::JobSystemContext::num_workers, Job::InitializationLock::num_workers_ready, Job::JobSystemContext::sys_arch_str, Job::JobSystemContext::system_alloc_alignment, Job::JobSystemContext::system_alloc_size, Job::JobSystemCreateOptions::worker_queue_size, and Job::JobSystemContext::workers.
Referenced by Initialize().
void Job::SetupUserThread | ( | ) |
Must be called in the callstack of the thread to be setup.
Sets up the state needed to be able to use the job system from this thread. The job system will not start up until all user threads have been setup.
Definition at line 856 of file job_system.cpp.
References g_JobSystem, JobAssert, Job::JobSystemContext::num_owned_workers, Job::JobSystemContext::num_user_threads_setup, and Job::JobSystemContext::workers.
|
noexcept |
An implementation defined name for the CPU architecture of the device. This function can be called by any thread concurrently.
Definition at line 923 of file job_system.cpp.
References g_JobSystem, and Job::JobSystemContext::sys_arch_str.
|
noexcept |
Returns the number of workers created by the system. This function can be called by any thread concurrently.
Definition at line 918 of file job_system.cpp.
References g_JobSystem, and Job::JobSystemContext::num_workers.
Referenced by Job::Splitter::EvenSplit(), and TaskSubmit().
|
noexcept |
The current id of the current thread. This function can be called by any thread concurrently.
The main thread will always be 0.
Definition at line 928 of file job_system.cpp.
References g_CurrentWorker, g_JobSystem, JobAssert, and Job::JobSystemContext::workers.
Referenced by WaitOnTask().
|
noexcept |
Allows for querying if we are currently executing in the main thread.
Definition at line 934 of file job_system.cpp.
References g_CurrentWorker, and IsMainThread().
Referenced by IsMainThread(), and Job::detail::mainQueueTryRunTask().
|
noexcept |
This will deallocate any memory used by the system and shutdown any threads created by 'bfJob::initialize'.
Definition at line 939 of file job_system.cpp.
References g_CurrentWorker, g_JobSystem, Job::JobSystemContext::is_running, JobAssert, Job::JobSystemContext::needs_delete, Job::JobSystemContext::num_owned_workers, Job::JobSystemContext::system_alloc_size, Job::JobSystemContext::worker_sleep_mutex, and Job::JobSystemContext::workers.
Creates a new Task that should be later submitted by calling 'TaskSubmit'.
function | The function you want run by the scheduler. |
parent | An optional parent Task used in conjunction with 'WaitOnTask' to force dependencies. |
Definition at line 986 of file job_system.cpp.
References g_JobSystem, JobAssert, and Job::JobSystemContext::num_tasks_per_worker.
Referenced by ParallelFor(), ParallelInvoke(), ParallelReduce(), and TaskMake().
Returns you the user-data buffer you way write to get data into your TaskFn.
task | The task whose user-data you want to grab. |
alignment | The required alignment the returned pointer must have. |
Definition at line 1023 of file job_system.cpp.
Referenced by TaskDataAs(), and TaskEmplaceData().
|
noexcept |
A 'continuation' is a task that will be added to a queue after the 'self' Task has finished running.
Continuations will be added to the same queue as the queue from the task that submits it.
self | The task to add the 'continuation' to. |
continuation | The Task to run after 'self' has finished. This task must not have already been submitted to a queue. |
queue | The queue you want the task to run on. |
Definition at line 1038 of file job_system.cpp.
References JobAssert, and k_InvalidQueueType.
|
noexcept |
Grabs the user-data pointer as the T you specified. No safety is guaranteed, this is just a dumb cast.
T | The type you want to receive the user-data buffer as. |
task | The task whose data you are retrieving. |
Definition at line 479 of file job_api.hpp.
References Job::TaskData::ptr, Job::TaskData::size, and TaskGetData().
void Job::TaskEmplaceData | ( | Task *const | task, |
Args &&... | args | ||
) |
Calls the constructor of T on the user-data buffer.
T | The type of T you want constructed in-place into the user-data buffer. |
Args | The Argument types passed into the T constructor. |
task | The task whose user-data buffer is affected. |
args | The arguments passed into the constructor of the user-data buffer casted as a T. |
Definition at line 487 of file job_api.hpp.
References JobAssert, Job::TaskData::ptr, Job::TaskData::size, and TaskGetData().
void Job::TaskSetData | ( | Task *const | task, |
const T & | data | ||
) |
Copies 'data' into the user-data buffer by calling the T copy constructor.
T | The data type that will be emplaced into the user-data buffer. |
task | The task whose user-data buffer is affected. |
data | The data copied into the user-data buffer. |
Definition at line 497 of file job_api.hpp.
void Job::TaskDestructData | ( | Task *const | task | ) |
Helper for calling destructor on the task's user data.
T | The expected type of the data, called ~T on the user data buffer. |
task | The task whose user-data buffer is affected. |
Definition at line 503 of file job_api.hpp.
Task * Job::TaskMake | ( | Closure && | function, |
Task *const | parent = nullptr |
||
) |
Creates a new task making a copy of the closure.
Closure | The type of the callable. |
function | The non pointer callable you want to store. |
parent | An optional parent Task used in conjunction with 'WaitOnTask' to force dependencies. |
Definition at line 509 of file job_api.hpp.
References Job::detail::taskGetPrivateUserData(), TaskMake(), and Job::detail::taskReservePrivateUserData().
|
noexcept |
Increments the task's ref count preventing it from being garbage collected.
This function should be called before taskSubmit
.
task | The task's who's ref count should be incremented. |
Definition at line 1053 of file job_system.cpp.
References JobAssert, and k_InvalidQueueType.
|
noexcept |
Decrements the task's ref count allow it to be garbage collected.
task | The task's who's ref count should be decremented. |
Definition at line 1061 of file job_system.cpp.
References JobAssert.
|
noexcept |
Returns the done status of the task.
This is only safe to call after submitting the task if you have an active reference to the task through a call to TaskIncRef.
task | The task to check whether or not it's done. |
Definition at line 1069 of file job_system.cpp.
Referenced by WaitOnTask().
|
noexcept |
Runs tasks from the main queue as long as there are tasks available and condition
returns true.
This function is not required to be called since the main queue will be evaluated during other calls to this API but allows for an easy way to flush the main queue guaranteeing a minimum latency.
ConditionFn | The type of the callable. Must be callable like: bool operator()(void); . |
condition | The function object indicating if the main queue should continue being evaluated. Will be called after a task has been completed. |
Definition at line 397 of file job_api.hpp.
References Job::detail::mainQueueTryRunTask().
Referenced by TickMainQueue().
|
inlinenoexcept |
Runs tasks from the main queue until it is empty.
This function is not required to be called since the main queue will be evaluated during other calls to this API but allows for an easy way to flush the main queue guaranteeing a minimum latency.
Definition at line 418 of file job_api.hpp.
References TickMainQueue().
|
noexcept |
Submits the task to the specified queue.
The Task is not required to have been created on the same thread that submits.
You may now wait on this task using 'WaitOnTask'.
self | The task to submit. |
queue | The queue you want the task to run on. |
Definition at line 1074 of file job_system.cpp.
References g_JobSystem, JobAssert, k_InvalidQueueType, MAIN, Job::JobSystemContext::main_queue, NORMAL, Job::JobSystemContext::num_available_jobs, NumWorkers(), Job::LockedQueue< T >::Push(), and WORKER.
Referenced by ParallelFor(), ParallelInvoke(), and TaskSubmitAndWait().
|
noexcept |
Waits until the specified task
is done executing. This function will block but do work while being blocked so there is no wasted time.
You may only call this function with a task created on the current 'Worker'.
It is a logic error to call this function on a task that has not been submitted (TaskSubmit).
task | The task to wait to finish executing. |
Definition at line 1144 of file job_system.cpp.
References CurrentWorker(), JobAssert, k_InvalidQueueType, and TaskIsDone().
Referenced by TaskSubmitAndWait().
|
noexcept |
Same as calling taskSubmit
followed by waitOnTask
.
self | The task to submit and wait to finish executing. |
queue | The queue you want the task to run on. |
Definition at line 1161 of file job_system.cpp.
References TaskSubmit(), and WaitOnTask().
Referenced by ParallelReduce().
|
noexcept |
CPU pause instruction to indicate when you are in a spin wait loop.
Definition at line 1198 of file job_system.cpp.
References NativePause.
Referenced by Job::MPMCQueue::Commit().
|
noexcept |
Asks the OS to yield this threads execution to another thread on the current cpu core.
Definition at line 1205 of file job_system.cpp.
Task * Job::ParallelFor | ( | const std::size_t | start, |
const std::size_t | count, | ||
S && | splitter, | ||
F && | fn, | ||
Task * | parent = nullptr |
||
) |
Parallel for algorithm, splits the work up recursively splitting based on the splitter
passed in.
Assumes all callable objects passed in can be invoked on multiple threads at the same time.
F | Type of function object passed in. Must be callable like: fn(Task* task, std::size_t index_range) |
S | Callable splitter, must be callable like: splitter(std::size_t count) |
start | Start index for the range to be parallelized. |
count | start + count defines the end range. |
splitter | Callable splitter, must be callable like: splitter(std::size_t count) |
fn | Function object must be callable like: fn(Job::Task* const task, const std::size_t index) |
parent | Parent task to add this task as a child of. |
Definition at line 608 of file job_api.hpp.
References ParallelFor(), TaskMake(), Job::detail::taskQType(), and TaskSubmit().
Referenced by ParallelFor(), and ParallelReduce().
Task * Job::ParallelReduce | ( | const std::size_t | start, |
const std::size_t | count, | ||
Splitter && | splitter, | ||
Reducer && | reduce, | ||
Task * | parent = nullptr |
||
) |
Definition at line 633 of file job_api.hpp.
References ParallelFor(), TaskMake(), Job::detail::taskQType(), and TaskSubmitAndWait().
Task * Job::ParallelFor | ( | T *const | data, |
const std::size_t | count, | ||
S && | splitter, | ||
F && | fn, | ||
Task * | parent = nullptr |
||
) |
Parallel for algorithm, splits the work up recursively splitting based on the splitter
passed in. This version is a helper for array data.
Assumes all callable objects passed in can be invoked on multiple threads at the same time.
T | Type of the array to process. |
F | Type of function object passed in. Must be callable like: fn(Task* task, IndexRange index_range) |
S | Callable splitter, must be callable like: splitter(std::size_t count) |
data | The start of the array to process. |
count | The number of elements in the data array. |
splitter | Callable splitter, must be callable like: splitter(std::size_t count) |
fn | Function object must be callable like: fn(job::Task* task, T* data_start, const std::size_t num_items) |
parent | Parent task to add this task as a child of. |
Definition at line 702 of file job_api.hpp.
References ParallelFor().
Invokes each passed in function object in parallel.
...F | The function objects types. Must be callable like: fn(Task* task) |
parent | Parent task to add this task as a child of. |
...fns | Function objects must be callable like: fn(Task* task) |
Definition at line 729 of file job_api.hpp.
References TaskMake(), Job::detail::taskQType(), and TaskSubmit().
|
staticconstexpr |
Definition at line 30 of file job_queue.hpp.
|
staticconstexpr |
Definition at line 74 of file job_system.cpp.
|
staticconstexpr |
Definition at line 77 of file job_system.cpp.
|
staticconstexpr |
Definition at line 78 of file job_system.cpp.
Referenced by TaskAddContinuation(), TaskIncRef(), TaskSubmit(), and WaitOnTask().
|
staticconstexpr |
Definition at line 89 of file job_system.cpp.
Referenced by Job::TaskPtr::isNull(), and Job::TaskPtr::TaskPtr().