Core Type Selector for Task Arena Constraints#

Note

To enable this feature, set the TBB_PREVIEW_TASK_ARENA_CORE_TYPE_SELECTOR macro to 1. When available and enabled, the feature-test macro TBB_HAS_TASK_ARENA_CORE_TYPE_SELECTOR is defined.

Description#

On a system with hybrid CPU cores, it is generally best to allow the OS to schedule threads across all core types. However, advanced users may occasionally need to constrain the scheduling. The constraints::set_core_type(core_type_id) API supports only a single core type, which may not allow selecting a suitable subset on processors with more than two core types.

The core type selector addresses this by allowing a callable object (selector) to rank every available core type. The selector is called once for each core type returned by tbb::info::core_types(). Positive scores indicate core types that should be used, negative scores exclude core types, and a score of zero means “use only if multi-core-type constraints are not supported” (see Selector Requirements for details).

This feature extends the tbb::task_arena and the tbb::info specifications with the following API:

  • Adds the selectable constant to task_arena.

  • Adds a new task_arena constructor template that accepts a constraints object together with a selector.

  • Adds a new task_arena::initialize template that accepts a constraints object together with a selector.

  • Adds a new info::default_concurrency template that returns the effective concurrency for given constraints and selector.

API#

Synopsis#

namespace oneapi {
    namespace tbb {

        class task_arena {
        public:
            static constexpr int selectable = /* unspecified */;

            template <typename Selector>
            task_arena(constraints a_constraints, Selector a_selector,
                       unsigned reserved_slots = 1,
                       priority a_priority = priority::normal);

            template <typename Selector>
            void initialize(constraints a_constraints, Selector a_selector,
                            unsigned reserved_slots = 1,
                            priority a_priority = priority::normal);
        }; // class task_arena

        namespace info {
            template <typename Selector>
            int default_concurrency(task_arena::constraints c, Selector a_selector);
        } // namespace info

    } // namespace tbb
} // namespace oneapi

Member Constants#

static constexpr int task_arena::selectable#

A special value for constraints::core_type that indicates the core type(s) should be determined by the selector provided to the constructor or initialize.

Member Functions#

template<typename Selector>
task_arena::task_arena(constraints a_constraints, Selector a_selector, unsigned reserved_slots = 1, priority a_priority = priority::normal)#

Requirements: Selector must be a callable type whose call operator accepts a std::tuple<tbb::core_type_id, std::size_t, std::size_t> and returns a value convertible to int. See Selector Requirements for the full contract.

Constructs a task_arena whose core type constraint is resolved by calling a_selector on every available core type. The a_constraints.core_type field should be set to task_arena::selectable; other constraint fields (numa_id, max_concurrency, max_threads_per_core) are applied as usual.

If a_constraints.core_type is not selectable, the selector is ignored and the constructor behaves identically to the existing constraints-based constructor.

template<typename Selector>
void task_arena::initialize(constraints a_constraints, Selector a_selector, unsigned reserved_slots = 1, priority a_priority = priority::normal)#

Overrides the arena settings and forces initialization. Core type resolution follows the same rules as the constructor above.

Functions#

template<typename Selector>
int info::default_concurrency(task_arena::constraints c, Selector a_selector)#

Requirements: Selector must be a callable type whose call operator accepts a std::tuple<tbb::core_type_id, std::size_t, std::size_t> and returns a value convertible to int. See Selector Requirements for the full contract.

Returns the number of threads that would be available for a task_arena created with the given constraints and selector. If c.core_type is selectable, the selector is applied to resolve the core type constraint before computing concurrency; otherwise, the selector is ignored and the function behaves identically to the single-argument info::default_concurrency.

Selector Requirements#

A selector is a callable object whose call operator has the following effective signature:

int operator()(std::tuple<tbb::core_type_id, std::size_t, std::size_t> core_type_info) const;

The tuple elements are:

Index

Type

Description

0

tbb::core_type_id

The core type identifier, as returned by tbb::info::core_types().

1

std::size_t

Zero-based position of this core type in the vector returned by tbb::info::core_types() (ordered from least to most performant).

2

std::size_t

Total number of available core types (tbb::info::core_types().size()).

The selector is called once for each core type. Its return value is interpreted as follows:

Score

Meaning

Positive

The core type is selected for use by the arena. A higher score indicates a stronger preference; if the implementation can only use a single core type, it selects the one with the highest score.

Zero

The core type is used only if multi-core-type constraints are not supported by the implementation. When there are positive scores and the rest are zero, the zeros act as a fallback to no constraint (rather than falling back to the single best-scored type).

Negative

The core type is excluded from use by the arena.

If all scores are negative, no core type constraint is applied. That is equivalent to setting constraints::core_type to automatic.

Example#

#define TBB_PREVIEW_TASK_ARENA_CORE_TYPE_SELECTOR 1

#include <oneapi/tbb/task_arena.h>
#include <oneapi/tbb/info.h>
#include <oneapi/tbb/parallel_for.h>

#include <cstdio>
#include <tuple>
#include <vector>

int main() {
    tbb::task_arena::constraints c;
    c.set_core_type(tbb::task_arena::selectable);
    auto selector = [](std::tuple<tbb::core_type_id, std::size_t, std::size_t> core_type) -> int {
        auto index = std::get<1>(core_type);
        auto total = std::get<2>(core_type);
        // Exclude the least performant type when there is more than one;
        // rank the rest by index (higher index = higher score).
        return (total > 1 && index == 0) ? -1 : static_cast<int>(index);
    };

    // Query the effective concurrency for these constraints and selector.
    int concurrency = tbb::info::default_concurrency(c, selector);
    std::printf("Effective concurrency: %d\n", concurrency);

    // Create a task arena that uses the selected core types.
    tbb::task_arena arena(c, selector);

    std::vector<double> data(1000);

    arena.execute([&data] {
        tbb::parallel_for(std::size_t(0), data.size(),
            [&data](std::size_t i) {
                data[i] = static_cast<double>(i * i);
            });
    });

    std::printf("data[999] = %.0f\n", data[999]);
}

In this example, a selector excludes the least performant core type (index 0) and ranks the remaining types by their position. The task_arena is created with constraints::core_type set to selectable, so the selector is invoked to determine which core types the arena may use. The info::default_concurrency overload with a selector is used to query the effective concurrency before creating the arena.