27#ifndef JOB_SYS_ASSERTIONS
28#define JOB_SYS_ASSERTIONS 1
32#define JobAssert(expr, msg) (::job::internal::AssertHandler)((expr), __FILE__, __LINE__, msg)
34#define JobAssert(expr, msg)
89 std::uint16_t num_threads = 0;
90 std::uint16_t normal_queue_size = 1024;
91 std::uint16_t worker_queue_size = 512;
92 std::uint64_t job_steal_rng_seed = 0u;
121 void Initialize(
const JobSystemMemoryRequirements& memory_requirements = {},
void*
const memory =
nullptr)
noexcept;
198 template<typename Closure>
199 void Dispatch(const
char* const name, Counter* const counter, const Closure& Callback, const
QueueMode queue =
QueueMode::Default) noexcept;
209 void WaitOn(const Counter& counter) noexcept;
229 void* src_user_data =
nullptr;
230 std::atomic_bool* is_ready_for_gc =
nullptr;
232 void ReleaseTaskToPool()
const;
237#if JOB_SYS_ASSERTIONS
238 void AssertHandler(
const bool condition,
const char*
const filename,
const int line_number,
const char*
const msg);
245 const std::size_t user_data_size,
246 const std::size_t user_data_alignment,
247 const void*
const user_data,
248 void (*InitUserData)(
void*
const user_data,
const void*
const in_user_data))
noexcept;
251 template<
typename Closure>
255 alignas(Closure)
unsigned char local_user_data[
sizeof(Closure)];
256 Closure*
const src_user_data =
static_cast<Closure*
>(ctx.src_user_data);
257 Closure*
const typed_callback = ::new (local_user_data) Closure(std::move(*src_user_data));
259 src_user_data->~Closure();
260 ctx.ReleaseTaskToPool();
262 (*typed_callback)(
static_cast<const job::Ctx&
>(ctx));
263 typed_callback->~Closure();
266 internal::DispatchImpl(name, counter, queue, ErasedCallback,
sizeof(Closure),
alignof(Closure), &Callback, +[](
void*
const dst_user_data,
const void*
const src_user_data) ->
void { ::new (dst_user_data) Closure(*
static_cast<const Closure*
>(src_user_data)); });
294 static Splitter EvenSplit(
const std::size_t total_num_items, std::size_t num_groups_per_thread = 1u)
296 if (num_groups_per_thread < 1u)
298 num_groups_per_thread = 1u;
312 return Splitter{max_data_size /
sizeof(T)};
352 template<
typename F,
typename S>
355 job::Dispatch(name, counter, [=, splitter = std::forward<S>(splitter), fn = std::forward<F>(fn)](
const job::Ctx& ctx) ->
void {
356 if (count > 1u && splitter(count))
358 const std::size_t left_count = count / 2;
359 const std::size_t right_count = count - left_count;
367 for (std::size_t offset = 0u; offset < count; ++offset)
369 child_ctx.
index = start + offset;
375 template<
typename T,
typename F,
typename S>
378 return job::ParallelFor(name, counter, std::size_t(0), count, std::forward<S>(splitter), [=](
const job::Ctx& ctx) { fn(ctx, data + ctx.
index); }, queue);
398 template<
typename... F>
401 (
job::Dispatch(name, counter, std::forward<F>(fns), queue), ...);
404 template<
typename Splitter,
typename Reducer>
407 const auto ParallelReduce_Impl = [=, splitter = std::forward<Splitter>(splitter), reduce = std::forward<Reducer>(reduce)](
const job::Ctx& ctx) ->
void {
414 std::size_t count_left = count;
416 while (count_left > 1)
418 const std::size_t stride = count_left / 2;
420 const auto ReduceRange = [stride, &reduce](
const job::Ctx& ctx,
const std::size_t index) ->
void {
421 reduce(ctx, index, index + stride);
425 ParallelFor(name, &c, start, stride, splitter, ReduceRange, queue);
428 if ((count_left & 1) != 0)
430 reduce(ctx, start, start + count_left - 1);
void(*)(const PrivateCtx &ctx) JobFn
void AssertHandler(const bool condition, const char *const filename, const int line_number, const char *const msg)
void DispatchImpl(const char *const name, Counter *const counter, const QueueMode queue, const JobFn func, const std::size_t user_data_size, const std::size_t user_data_alignment, const void *const user_data, void(*InitUserData)(void *const user_data, const void *const in_user_data)) noexcept
void ParallelInvoke(const char *const name, Counter *const counter, const QueueMode queue, F &&... fns)
Invokes each passed in function object in parallel.
void WaitOn(const Counter &counter) noexcept
Blocks until all tasks associated with counter are done while This function will block but do work wh...
void Shutdown() noexcept
This will deallocate any memory used by the system and shutdown any threads created by 'bfjob::initia...
void ParallelReduce(const char *const name, Counter *const counter, const std::size_t start, const std::size_t count, Splitter &&splitter, Reducer &&reduce, const QueueMode queue=QueueMode::Default)
bool IsMainThread() noexcept
Allows for querying if we are currently executing in the main thread.
void ParallelFor(const char *const name, Counter *const counter, const std::size_t start, const std::size_t count, S &&splitter, F &&fn, const QueueMode queue=QueueMode::Default)
Parallel for algorithm, splits the work up recursively splitting based on the splitter passed in.
QueueMode
Determines which threads the task will be allowed to run on.
@ Default
Tasks in this queue will run on either the main or worker threads.
@ WorkerOnly
Tasks in this queue will never run on the main thread.
void Dispatch(const char *const name, Counter *const counter, const Closure &Callback, const QueueMode queue=QueueMode::Default) noexcept
Main API entrypoint, Pushes a task onto the queue.
std::atomic_uint64_t unfinished_tasks
std::uint16_t WorkerID
The id type of each worker thread.
WorkerID CurrentWorker() noexcept
The current id of the current thread. This function can be called by any thread concurrently.
const char * ProcessorArchitectureName() noexcept
An implementation defined name for the CPU architecture of the device. This function can be called by...
std::uint16_t NumWorkers() noexcept
Returns the number of workers created by the system. This function can be called by any thread concur...
void PauseProcessor() noexcept
CPU pause instruction to indicate when you are in a spin wait loop.
void 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...
std::size_t NumSystemThreads() noexcept
Makes system calls to grab the number threads / processors on the device. This function can be called...
void YieldTimeSlice() noexcept
Asks the OS to yield this threads execution to another thread on the current cpu core.
The only syncronization mechanism. Allows you to wait on tasks you asssociated with this counter.
The runtime configuration for the Job System.
std::size_t index
Index for ParallelFor jobs, always zero for regular Dispatch.
WorkerID current_worker
The worker the current task is running on.
Counter * task_counter
The counter this task will decrement when done.
const char * task_name
The debug name of the task.
The memory requirements for a given configuration JobSystemCreateOptions.
std::size_t byte_size
The number of bytes the job system needed.
JobSystemMemoryRequirements(const JobSystemCreateOptions &options={}) noexcept
std::size_t alignment
The base alignment the pointer should be.
JobSystemCreateOptions options
The options used to create the memory requirements.
static constexpr Splitter MaxItemsPerTask(const std::size_t max_items)
constexpr bool operator()(const std::size_t count) const
static Splitter EvenSplit(const std::size_t total_num_items, std::size_t num_groups_per_thread=1u)
Splits work evenly across the threads depending on the number of workers.
static constexpr Splitter MaxDataSize(const std::size_t max_data_size)