Custom Policies
The dynamic selection API is an experimental feature in the Intel® 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...) - Returns std::optional<selection_type>, empty if no resource available
initialize_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 resource
Returns std::nullopt if no resource is currently available
May 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 represents
get_policy() - Returns the policy that created the selection
optionalreport(i) and report(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 |
|---|---|---|---|
task_submission_t |
task_submission |
void |
Signals when a task is submitted |
task_completion_t |
task_completion |
void |
Signals when a task completes |
task_time_t |
task_time |
std::chrono::milliseconds |
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 |
|---|---|
fixed_resource_policy |
None |
round_robin_policy |
None |
dynamic_load_policy |
task_submission, task_completion |
auto_tune_policy |
task_time |
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.