13#include <cuda_runtime_api.h>
14#include <oneapi/tbb/task_arena.h>
15#include <oneapi/tbb/task_group.h>
16#include <oneapi/tbb/task_scheduler_observer.h>
20#include <uni20/config.hpp>
34[[noreturn]]
inline void cuda_arena_observer_failure(cudaError_t status,
char const* operation,
int device)
36 PANIC(
"CUDA arena observer operation failed", operation, device, cudaGetErrorName(status),
37 cudaGetErrorString(status));
40inline void check_cuda_arena_observer(cudaError_t status,
char const* operation,
int device)
noexcept
42 if (status != cudaSuccess) cuda_arena_observer_failure(status, operation, device);
46class CudaArenaObserver final :
public oneapi::tbb::task_scheduler_observer {
48 CudaArenaObserver(oneapi::tbb::task_arena& arena,
int device)
49 : oneapi::tbb::task_scheduler_observer(arena), device_(device)
52 CudaArenaObserver(CudaArenaObserver
const&) =
delete;
53 CudaArenaObserver& operator=(CudaArenaObserver
const&) =
delete;
55 ~CudaArenaObserver()
override { this->stop(); }
57 void start() { this->observe(
true); }
61 if (this->is_observing()) this->observe(
false);
67 CudaArenaObserver
const* observer;
71 static std::vector<Selection>& selection_stack()
73 static thread_local std::vector<Selection> stack;
77 void on_scheduler_entry(
bool)
noexcept override
79 int previous_device = -1;
80 check_cuda_arena_observer(cudaGetDevice(&previous_device),
"cudaGetDevice on arena entry", device_);
81 selection_stack().push_back(Selection{
this, previous_device});
82 check_cuda_arena_observer(cudaSetDevice(device_),
"cudaSetDevice on arena entry", device_);
85 void on_scheduler_exit(
bool)
noexcept override
87 auto& stack = selection_stack();
88 CHECK(!stack.empty());
90 int const previous_device = stack.back().previous_device;
92 check_cuda_arena_observer(cudaSetDevice(previous_device),
"cudaSetDevice on arena exit", previous_device);
98inline void verify_tbb_cuda_device(
int expected_device)
101 int current_device = -1;
102 check_cuda_arena_observer(cudaGetDevice(¤t_device),
"cudaGetDevice before coroutine resume", expected_device);
105 static_cast<void>(expected_device);
144 device_arenas_.reserve(devices.size());
145 for (
auto device : devices)
147 CHECK(!this->find_device_arena(device.ordinal()),
"CUDA device enrolled more than once", device.ordinal());
148 device_arenas_.push_back(std::make_unique<DeviceArena>(device, options.cuda_max_concurrency_per_device));
151 if (options.default_cuda_device)
153 default_device_ = this->device_arena(*options.default_cuda_device).device.ordinal();
155 else if (!device_arenas_.empty())
157 default_device_ = device_arenas_.front()->device.ordinal();
172 CHECK(default_device_,
"cannot schedule an unbound CUDA task without an enrolled default device");
174 if (task.set_scheduler(
this)) this->
enqueue_task(std::move(task));
182 auto& device_arena = this->device_arena(device);
185 if (task.set_scheduler(
this)) this->
enqueue_task(std::move(task));
189 [[nodiscard]]
bool has_device(
int device)
const noexcept {
return this->find_device_arena(device) !=
nullptr; }
192 bool accepts_route(
TaskRoute route)
const noexcept override
196 auto const device = route.cuda_device ? route.cuda_device : default_device_;
202 DeviceArena(
cuda::Device selected_device,
int max_concurrency)
203 : device(selected_device), arena(max_concurrency, 0),
204 observer(arena, device.ordinal())
209 cuda::check(cudaSetDevice(device.ordinal()),
"cudaSetDevice for TBB CUDA scheduler initialization",
215 ~DeviceArena() { observer.stop(); }
218 oneapi::tbb::task_arena arena;
219 detail::CudaArenaObserver observer;
222 bool can_direct_transfer(TaskHandle from, TaskHandle to)
const noexcept override
225 auto const from_device = this->activation_device(from);
226 auto const to_device = this->activation_device(to);
227 return from_device && from_device == to_device && this->
has_device(*from_device);
230 void dispatch_handle(TaskHandle
handle)
override
238 auto const device = this->activation_device(
handle);
239 CHECK(device,
"CUDA task has no affinity and the scheduler has no default CUDA device");
240 auto& selected = this->device_arena(*device);
242 [device = *device] { detail::verify_tbb_cuda_device(device); });
245 [[nodiscard]] DeviceArena* find_device_arena(
int device)
const noexcept
247 auto const found = std::find_if(device_arenas_.begin(), device_arenas_.end(), [device](
auto const& candidate) {
248 return candidate->device.ordinal() == device;
250 return found == device_arenas_.end() ? nullptr : found->get();
253 DeviceArena& device_arena(
int device)
255 auto* result = this->find_device_arena(device);
256 CHECK(result,
"CUDA task submitted to a device not enrolled in this scheduler", device);
260 [[nodiscard]] std::optional<int> activation_device(TaskHandle task)
const noexcept
262 if (
auto const affinity =
cuda_promise(task).device())
return affinity;
263 return default_device_;
266 std::vector<std::unique_ptr<DeviceArena>> device_arenas_;
267 std::optional<int> default_device_{};
static void record_task_scheduled(std::coroutine_handle<> h)
Records the call stack where a coroutine was submitted to a scheduler.
void bind_device(int device)
Bind the CUDA device before the coroutine starts.
Definition async_task_promise.hpp:673
CUDA-constrained public task type used for typed initial admission.
Definition cuda_task.hpp:14
Scheduler interface for initial CudaTask admission.
Definition cuda_task.hpp:49
Unified oneTBB scheduler with one host arena and one arena per CUDA device.
Definition tbb_cuda_scheduler.hpp:130
bool has_device(int device) const noexcept
Report whether a CUDA device has an execution arena in this scheduler.
Definition tbb_cuda_scheduler.hpp:189
TbbCudaScheduler(TbbCudaScheduler const &)=delete
void schedule(CudaTask &&task, int device) override
Bind and submit a CUDA task to an enrolled device arena.
Definition tbb_cuda_scheduler.hpp:180
void schedule(CudaTask &&task) override
Submit a CUDA task without establishing device affinity.
Definition tbb_cuda_scheduler.hpp:170
TbbCudaScheduler & operator=(TbbCudaScheduler const &)=delete
~TbbCudaScheduler() noexcept override
Definition tbb_cuda_scheduler.hpp:164
TbbCudaScheduler(std::vector< cuda::Device > devices, TbbCudaSchedulerOptions options={})
Construct a scheduler for an explicit set of CUDA devices.
Definition tbb_cuda_scheduler.hpp:141
TbbCudaScheduler(TbbCudaSchedulerOptions options={})
Construct a scheduler enrolling every visible CUDA device.
Definition tbb_cuda_scheduler.hpp:134
Definition tbb_scheduler.hpp:59
void dispatch_handle_in_arena(oneapi::tbb::task_arena &arena, TaskHandle handle, detail::TbbTaskAdmissionContext context, Prepare &&prepare)
Submit one activation to an arena through the shared task group.
Definition tbb_scheduler.hpp:320
void enqueue_task(BasicTask &&t)
Enqueue one task through the scheduler's domain-routing hook.
Definition tbb_scheduler.hpp:281
void wait_for_submitted_tasks()
Wait for every activation registered with the shared task group.
Definition tbb_scheduler.hpp:275
virtual void dispatch_handle(TaskHandle handle)
Route one runnable task activation to its execution arena.
Definition tbb_scheduler.hpp:308
void schedule(AsyncTask &&t) override
Schedule a coroutine for initial execution.
Definition tbb_scheduler.hpp:96
Validated CUDA device identity with cached immutable capabilities.
Definition device.hpp:105
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
cuda::ResourceLease< HandleSlot > handle
Definition cublas/execution.cpp:33
Structured CUDA runtime failures and checked-call helpers.
Defines the CUDA task admission type and scheduler interface.
CUDA device discovery and immutable hardware capability snapshots.
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
CudaTaskPromise & cuda_promise(TaskHandle handle) noexcept
Narrow an erased task promise after verifying that it is CUDA-specific.
Definition async_task_promise.hpp:707
void check(cudaError_t status, std::string_view operation, std::optional< int > device, std::source_location where)
Raise when a CUDA runtime operation did not return cudaSuccess.
Definition cuda_error.cpp:44
CUDA device, stream, resource, and scoped runtime primitives.
Execution requirements used to validate a scheduler route.
Definition async_task.hpp:43
Arena configuration for the unified host/CUDA TBB scheduler.
Definition tbb_cuda_scheduler.hpp:113
int host_max_concurrency
Maximum participation in the host arena.
Definition tbb_cuda_scheduler.hpp:115
int cuda_max_concurrency_per_device
Maximum worker participation in each CUDA device arena.
Definition tbb_cuda_scheduler.hpp:119
std::optional< int > default_cuda_device
Device used to route CUDA tasks that have not established affinity.
Definition tbb_cuda_scheduler.hpp:122
Selects the task-registry implementation for the current build mode.
Scheduler implementation using oneAPI oneTBB task_arena + task_group.
Internal non-blocking oneTBB arena admission helper.
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309
#define DEBUG_CHECK_EQUAL(a, b,...)
Definition trace.hpp:618