Custom Policies#
The dynamic selection API is an experimental feature in the oneAPI DPC++ Library (oneDPL) that selects an execution resource based on a chosen selection policy. While several policies are provided out of the box, you can create custom policies to implement application-specific selection strategies.
Using policy_base#
The recommended approach for creating custom policies is to inherit from policy_base,
which provides default implementations of submission and initialization logic.
policy_base uses the Curiously Recurring Template Pattern (CRTP), where the derived
policy class passes itself as the first template parameter. This allows policy_base
to call derived class methods (like try_select) without virtual function overhead.
namespace oneapi::dpl::experimental {
template<typename Policy, typename ResourceAdapter, typename Backend,
typename... ReportReqs>
class policy_base {
public:
using resource_type = /* backend resource type */;
// Initialization
void initialize();
void initialize(const std::vector<resource_type>& u);
void initialize(const std::vector<resource_type>& u,
ResourceAdapter adapter, Args... args);
// Submission operations
auto submit(Function&& f, Args&&... args); // Retries until success
void submit_and_wait(Function&& f, Args&&... args); // Blocks until complete
auto try_submit(Function&& f, Args&&... args); // Returns std::optional
// Queries
auto get_resources() const;
auto get_submission_group();
protected:
std::shared_ptr<Backend> backend_;
};
}
When using policy_base, your custom policy must implement:
try_select(Args...)- Returnsstd::optional<selection_type>, empty if no resource availableinitialize_state(Args...)- Performs policy-specific initialization
The policy_base automatically provides the required backend_type and resource_type aliases.
Initialization#
The initialize_state() function is called after the backend is initialized.
Use it to set up policy-specific state using resources from get_resources().
Selection Logic#
The try_select() function implements your selection algorithm:
Returns
std::optional<selection_type>with selected resourceReturns
std::nulloptif no resource is currently availableMay accept additional arguments for selection hints
Selection Type#
The selection_type represents a selected resource and encapsulates the policy and
resource information. It must satisfy the Selection requirements:
unwrap()- Returns the resource object (e.g.,sycl::queue) the selection representsget_policy()- Returns the policy that created the selectionoptional
report(i)andreport(i, v)- Report execution information back to the policy if required
For policies that do not require execution information reporting (such as simple
round_robin_policy), you may use the provided basic_selection_handle_t:
template<typename Policy, typename Resource>
class basic_selection_handle_t {
public:
explicit basic_selection_handle_t(const Policy& p, Resource e);
Resource unwrap();
Policy get_policy();
};
For policies that need execution information (like dynamic_load_policy which tracks
task submissions and completions, or auto_tune_policy which measures task timing),
define a custom selection type with report() methods, as is shown in the following
example:
template<typename Policy, typename Backend>
class custom_selection_handle_t {
Policy policy_;
resource_type resource_;
using scratch_space_t =
typename backend_traits<Backend>::template selection_scratch_t<
execution_info::task_submission_t, execution_info::task_completion_t>;
scratch_space_t scratch_space;
public:
custom_selection_handle_t(const Policy& p, resource_type r)
: policy_(p), resource_(std::move(r)) {}
auto unwrap() { return oneapi::dpl::experimental::unwrap(resource_); }
Policy get_policy() { return policy_; }
// Report execution events
void report(const execution_info::task_submission_t&) const {
// Handle task submission event
}
void report(const execution_info::task_completion_t&) const {
// Handle task completion event
}
};
The backend will call the selection handle’s report() methods when execution
events occur, allowing the policy to update its state accordingly.
As shown above, for policies that need execution information, the selection
handle must also include a member named scratch_space with type dictated by
backend_traits<Backend>::template selection_scratch_t<Reqs...> where Reqs...
is a variadic pack of all execution information requirements. The backend will
use this scratch_space member to store temporary instrumentation data (like
profiling events) needed to satisfy the reporting requirement. For more
information, see Selection Scratch Space.
Reporting Requirements#
If your policy needs execution information (like task completion times), specify
reporting requirements as template parameters to policy_base:
class timing_aware_policy
: public ex::policy_base<timing_aware_policy,
oneapi::dpl::identity,
ex::default_backend<sycl::queue>,
ex::execution_info::task_time_t> {
// Policy implementation that receives timing information
};
Execution Information#
Backends can provide execution information to policies for making informed selection
decisions. The oneapi::dpl::experimental::execution_info namespace contains
tag types and tag objects that describe the instrumentation information policies
require for their selection logic. Policies specify their requirements using these
tags during backend construction. Backends then call report with these tags
to provide the requested execution information to the policy via
selection objects.
The following execution information types are available:
Tag Type |
Tag Object |
Value Type |
Description |
|---|---|---|---|
|
|
void |
Signals when a task is submitted |
|
|
void |
Signals when a task completes |
|
|
|
Elapsed time from submission to completion |
Built-In Policy Requirements#
The following table shows the reporting requirements for each built-in policy:
Policy |
Reporting Requirements |
|---|---|
|
None |
|
None |
|
|
|
|
Policies with no reporting requirements can work with any backend, including
the provided generic backend implementation which is used when no specialization
of core_resource_backend exists for the specific resource. Policies with
reporting requirements need a backend that supports those specific types of
execution information.
Policies with reporting requirements must call lazy_report() prior to selection,
if the backend supports it. Lazy Reporting allows backends
to update their execution information state before making selection decisions.
See dynamic_load_policy and auto_tune_policy for examples of this.
Policy State Reference Semantics#
Best practice is to make your policy’s selection state stored in a shared_ptr to enable
common reference semantics - copies of your policy will share the same state, as set up by
initialize_state calls.
struct selector_t {
// Policy-specific selection state
};
std::shared_ptr<selector_t> selector_;
Examples#
For examples please look to the existing policies within oneDPL (fixed_resource_policy,
round_robin_policy, dynamic_load_policy, auto_tune_policy), which are
all written using policy_base and according to these best practices.