1 /*
2 Copyright (c) 2019-2023 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #ifndef __TBB_test_common_arena_constraints_H_
18 #define __TBB_test_common_arena_constraints_H_
19
20 #if _WIN32 || _WIN64
21 #define _CRT_SECURE_NO_WARNINGS
22 #endif
23
24 #include "common/test.h"
25 #include "common/spin_barrier.h"
26 #include "common/utils.h"
27 #include "common/memory_usage.h"
28 #include "common/utils_concurrency_limit.h"
29
30 #include "oneapi/tbb/task_arena.h"
31 #include "oneapi/tbb/spin_mutex.h"
32
33 #include <vector>
34 #include <unordered_set>
35
36 #if (_WIN32 || _WIN64) && __TBB_HWLOC_VALID_ENVIRONMENT
37 #include <windows.h>
get_processors_group_count()38 int get_processors_group_count() {
39 SYSTEM_INFO si;
40 GetNativeSystemInfo(&si);
41 DWORD_PTR pam, sam, m = 1;
42 GetProcessAffinityMask( GetCurrentProcess(), &pam, &sam );
43 int nproc = 0;
44 for ( std::size_t i = 0; i < sizeof(DWORD_PTR) * CHAR_BIT; ++i, m <<= 1 ) {
45 if ( pam & m )
46 ++nproc;
47 }
48 // Setting up processor groups in case the process does not restrict affinity mask and more than one processor group is present
49 if ( nproc == (int)si.dwNumberOfProcessors ) {
50 // The process does not have restricting affinity mask and multiple processor groups are possible
51 return (int)GetActiveProcessorGroupCount();
52 } else {
53 return 1;
54 }
55 }
56 #else
get_processors_group_count()57 int get_processors_group_count() { return 1; }
58 #endif
59
60 //TODO: Write a test that checks for memory leaks during dynamic link/unlink of TBBbind.
61 #if __TBB_HWLOC_VALID_ENVIRONMENT
62 #include "oneapi/tbb/concurrent_unordered_set.h"
63
64 #include <atomic>
65 #include <algorithm>
66
67 #if _MSC_VER
68 #if __clang__
69 #pragma GCC diagnostic push
70 #pragma GCC diagnostic ignored "-Wunused-parameter"
71 #else
72 #pragma warning( push )
73 #pragma warning( disable : 4100 )
74 #endif
75 #endif
76 #include <hwloc.h>
77 #if _MSC_VER
78 #if __clang__
79 #pragma GCC diagnostic pop
80 #else
81 #pragma warning( pop )
82 #endif
83 #endif
84
85 #define __HWLOC_HYBRID_CPUS_INTERFACES_PRESENT (HWLOC_API_VERSION >= 0x20400)
86 #define __HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING_PRESENT (HWLOC_API_VERSION >= 0x20500)
87 // At this moment the hybrid CPUs HWLOC interfaces returns unexpected results on some Windows machines
88 // in the 32-bit arch mode.
89 #define __HWLOC_HYBRID_CPUS_INTERFACES_VALID (!_WIN32 || _WIN64)
90
91 #define __HYBRID_CPUS_TESTING __HWLOC_HYBRID_CPUS_INTERFACES_PRESENT && __HWLOC_HYBRID_CPUS_INTERFACES_VALID
92 #define __HWLOC_CPUBIND_PRESENT (!__APPLE__)
93
94 // Macro to check hwloc interfaces return codes
95 #define hwloc_require_ex(command, ...) \
96 REQUIRE_MESSAGE(command(__VA_ARGS__) >= 0, "Error occurred inside hwloc call.");
97
98 struct index_info {
99 int index{-1};
100 int concurrency{-1};
101 hwloc_bitmap_t cpuset{nullptr};
102
103 index_info() = default;
index_infoindex_info104 index_info(const index_info& src)
105 : index{src.index}
106 , concurrency{src.concurrency}
107 , cpuset{hwloc_bitmap_dup(src.cpuset)}
108 {}
109
110 index_info& operator=(index_info src) {
111 index = src.index;
112 concurrency = src.concurrency;
113 std::swap(cpuset, src.cpuset);
114 return *this;
115 }
116
~index_infoindex_info117 ~index_info() {
118 hwloc_bitmap_free(cpuset);
119 }
120 };
121
122 struct core_info {
123 hwloc_bitmap_t cpuset{nullptr};
124
125 core_info() = default;
core_infocore_info126 core_info(hwloc_bitmap_t _cpuset)
127 : cpuset{hwloc_bitmap_dup(_cpuset)}
128 {}
core_infocore_info129 core_info(const core_info& src)
130 : cpuset{hwloc_bitmap_dup(src.cpuset)}
131 {}
132
133 core_info& operator=(core_info src) {
134 std::swap(cpuset, src.cpuset);
135 return *this;
136 }
137
~core_infocore_info138 ~core_info() {
139 hwloc_bitmap_free(cpuset);
140 }
141 };
142
143 class system_info {
144 hwloc_topology_t topology;
145 hwloc_cpuset_t process_cpuset{nullptr};
146
147 std::vector<index_info> numa_node_infos{};
148 std::vector<index_info> cpu_kind_infos{};
149 std::vector<core_info> core_infos{};
150
151 // hwloc_cpuset_t and hwloc_nodeset_t (inherited from hwloc_bitmap_t ) is pointers,
152 // so we must manage memory allocation and deallocation
153 using memory_handler_t = tbb::concurrent_unordered_set<hwloc_bitmap_t>;
154 memory_handler_t memory_handler{};
155
156 static system_info* system_info_ptr;
157
158 public:
initialize()159 static void initialize() {
160 static system_info topology_instance;
161 system_info_ptr = &topology_instance;
162 }
163
164 private:
instance()165 static system_info& instance() {
166 REQUIRE_MESSAGE(system_info_ptr, "Get access to the uninitialize system info.(reference)");
167 return *system_info_ptr;
168 }
169
system_info()170 system_info() {
171 hwloc_require_ex(hwloc_topology_init, &topology);
172 #if __HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING_PRESENT
173 if ( get_processors_group_count() == 1 ) {
174 REQUIRE(
175 hwloc_topology_set_flags(topology,
176 HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM |
177 HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) == 0
178 );
179 }
180 #endif
181 hwloc_require_ex(hwloc_topology_load, topology);
182
183 #if __HWLOC_CPUBIND_PRESENT
184 if ( get_processors_group_count() > 1 ) {
185 process_cpuset = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset(topology));
186 } else {
187 process_cpuset = hwloc_bitmap_alloc();
188 hwloc_require_ex(hwloc_get_cpubind, topology, process_cpuset, 0);
189 }
190 #else
191 process_cpuset = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset(topology));
192 #endif
193
194 hwloc_obj_t current_numa_node = nullptr;
195 index_info current_node_info{};
196 while ((current_numa_node = hwloc_get_next_obj_by_type(topology,
197 HWLOC_OBJ_NUMANODE,
198 current_numa_node)) != nullptr) {
199 current_node_info.index = static_cast<int>(current_numa_node->logical_index);
200 current_node_info.cpuset = hwloc_bitmap_dup(current_numa_node->cpuset);
201 hwloc_bitmap_and(current_node_info.cpuset, current_node_info.cpuset, process_cpuset);
202 current_node_info.concurrency = hwloc_bitmap_weight(current_node_info.cpuset);
203 if(current_node_info.concurrency) {
204 numa_node_infos.push_back(current_node_info);
205 }
206 }
207
208 if (numa_node_infos.empty()) {
209 current_node_info.index = 0;
210 current_node_info.cpuset = hwloc_bitmap_dup(process_cpuset);
211 current_node_info.concurrency = hwloc_bitmap_weight(process_cpuset);
212 numa_node_infos.push_back(current_node_info);
213 }
214
215 std::sort(numa_node_infos.begin(), numa_node_infos.end(),
216 [](const index_info& a, const index_info& b) {
217 return a.index < b.index;
218 }
219 );
220
221 bool core_types_parsing_broken = true;
222 #if __HYBRID_CPUS_TESTING
223 // Getting CPU kinds info
224 auto num_cpu_kinds = hwloc_cpukinds_get_nr(topology, 0);
225 REQUIRE_MESSAGE(num_cpu_kinds >= 0,
226 "HWLOC cannot detect the number of cpukinds.(reference)");
227
228 core_types_parsing_broken = num_cpu_kinds == 0;
229 int current_efficiency = -1;
230 cpu_kind_infos.resize(num_cpu_kinds);
231 for (auto kind_index = 0; kind_index < num_cpu_kinds; ++kind_index) {
232 auto& cki = cpu_kind_infos[kind_index];
233
234 cki.cpuset = hwloc_bitmap_alloc();
235 CHECK_MESSAGE(
236 cki.cpuset,
237 "HWLOC was unable to allocate bitmap. Following checks might fail.(reference)"
238 );
239
240 hwloc_require_ex(
241 hwloc_cpukinds_get_info, topology, kind_index, cki.cpuset, ¤t_efficiency,
242 /*nr_infos*/nullptr, /*infos*/nullptr, /*flags*/0
243 );
244 if (current_efficiency < 0) {
245 core_types_parsing_broken = true;
246 break;
247 }
248 hwloc_bitmap_and(cki.cpuset, cki.cpuset, process_cpuset);
249
250 cki.index = hwloc_cpukinds_get_by_cpuset(topology, cki.cpuset, /*flags*/0);
251 REQUIRE_MESSAGE(cki.index >= 0,
252 "hwloc failed obtaining kind index via cpuset.(reference)");
253
254 cki.concurrency = hwloc_bitmap_weight(cki.cpuset);
255 }
256 #endif /*__HYBRID_CPUS_TESTING*/
257
258 if (core_types_parsing_broken) {
259 cpu_kind_infos.resize(1);
260 cpu_kind_infos[0].index = -1;
261 cpu_kind_infos[0].cpuset = hwloc_bitmap_dup(process_cpuset);
262 cpu_kind_infos[0].concurrency = hwloc_bitmap_weight(process_cpuset);
263 }
264
265 hwloc_bitmap_t core_affinity = hwloc_bitmap_alloc();
266 hwloc_obj_t current_core = nullptr;
267 while ((current_core = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_CORE, current_core)) != nullptr) {
268 hwloc_bitmap_and(core_affinity, process_cpuset, current_core->cpuset);
269 if (hwloc_bitmap_weight(core_affinity) > 0) {
270 core_infos.emplace_back(core_affinity);
271 }
272 }
273 hwloc_bitmap_free(core_affinity);
274
275 testing_reference_topology_parsing_validation();
276 }
277
~system_info()278 ~system_info() {
279 for (auto& allocated_mask: memory_handler) {
280 hwloc_bitmap_free(allocated_mask);
281 }
282 hwloc_topology_destroy(topology);
283
284 hwloc_bitmap_free(process_cpuset);
285 }
286
testing_reference_topology_parsing_validation()287 void testing_reference_topology_parsing_validation() {
288 hwloc_cpuset_t buffer_cpu_set = hwloc_bitmap_alloc();
289
290 REQUIRE_MESSAGE(numa_node_infos.size() > 0, "Negative NUMA nodes count.(reference)");
291 REQUIRE_MESSAGE(cpu_kind_infos.size() > 0, "Negative core types count.(reference)");
292 REQUIRE_MESSAGE(core_infos.size() > 0, "Negative available cores count.(reference)");
293 #if NUMA_NODES_NUMBER
294 REQUIRE_MESSAGE(numa_node_infos.size() == NUMA_NODES_NUMBER,
295 "Manual NUMA nodes count validation fails.(reference)");
296 #endif /*NUMA_NODES_NUMBER*/
297 #if CORE_TYPES_NUMBER
298 REQUIRE_MESSAGE(cpu_kind_infos.size() == CORE_TYPES_NUMBER,
299 "Manual core types count validation fails.(reference)");
300 #endif /*CORE_TYPES_NUMBER*/
301
302 // NUMA topology verification
303 hwloc_bitmap_zero(buffer_cpu_set);
304 for (const auto& numa_node_info: numa_node_infos) {
305 REQUIRE_MESSAGE(!hwloc_bitmap_intersects(buffer_cpu_set, numa_node_info.cpuset),
306 "NUMA nodes related CPUset have the same bits. "
307 "It seems like error during HWLOC topology parsing.(reference)");
308 hwloc_bitmap_or(buffer_cpu_set, buffer_cpu_set, numa_node_info.cpuset);
309 }
310 REQUIRE_MESSAGE( hwloc_bitmap_isequal(buffer_cpu_set, process_cpuset),
311 "Intersected NUMA nodes masks should be equal to process affinity.(reference)");
312
313 // Core types topology verification
314 hwloc_bitmap_zero(buffer_cpu_set);
315 for (const auto& cpu_kind_info: cpu_kind_infos) {
316 REQUIRE_FALSE_MESSAGE(hwloc_bitmap_intersects(buffer_cpu_set, cpu_kind_info.cpuset),
317 "core types related CPUset have the same bits. "
318 "It seems like error during HWLOC topology parsing.(reference)");
319 hwloc_bitmap_or(buffer_cpu_set, buffer_cpu_set, cpu_kind_info.cpuset);
320 }
321 REQUIRE_MESSAGE(hwloc_bitmap_isequal(buffer_cpu_set, process_cpuset),
322 "Intersected core type masks should be equal to process affinity.(reference)");
323
324 hwloc_bitmap_free(buffer_cpu_set);
325 }
326
327 public:
328 typedef hwloc_bitmap_t affinity_mask;
329 typedef hwloc_const_bitmap_t const_affinity_mask;
330
get_process_affinity_mask()331 static hwloc_const_bitmap_t get_process_affinity_mask() {
332 return instance().process_cpuset;
333 }
334
get_maximal_threads_per_core()335 static std::size_t get_maximal_threads_per_core() {
336 auto max_threads_it =
337 std::max_element(
338 std::begin(instance().core_infos), std::end(instance().core_infos),
339 [](const core_info& first, const core_info& second) {
340 return hwloc_bitmap_weight(first.cpuset) < hwloc_bitmap_weight(second.cpuset);
341 }
342 );
343 __TBB_ASSERT(hwloc_bitmap_weight(max_threads_it->cpuset) > 0,
344 "Not positive maximal threads per core value.(reference)");
345 return hwloc_bitmap_weight(max_threads_it->cpuset);
346 }
347
allocate_empty_affinity_mask()348 static affinity_mask allocate_empty_affinity_mask() {
349 affinity_mask result = hwloc_bitmap_alloc();
350 instance().memory_handler.insert(result);
351 return result;
352 }
353
allocate_current_affinity_mask()354 static affinity_mask allocate_current_affinity_mask() {
355 affinity_mask result = hwloc_bitmap_alloc();
356 instance().memory_handler.insert(result);
357 #if __HWLOC_CPUBIND_PRESENT
358 hwloc_require_ex(hwloc_get_cpubind, instance().topology, result, HWLOC_CPUBIND_THREAD);
359 #else
360 hwloc_bitmap_copy(result, hwloc_topology_get_complete_cpuset(instance().topology));
361 #endif
362 REQUIRE_MESSAGE(!hwloc_bitmap_iszero(result), "Empty current affinity mask.");
363 return result;
364 }
365
get_cpu_kinds_info()366 static std::vector<index_info> get_cpu_kinds_info() {
367 return instance().cpu_kind_infos;
368 }
369
get_numa_nodes_info()370 static std::vector<index_info> get_numa_nodes_info() {
371 return instance().numa_node_infos;
372 }
373
get_cores_info()374 static std::vector<core_info> get_cores_info() {
375 return instance().core_infos;
376 }
377
get_available_max_threads_values()378 static std::vector<int> get_available_max_threads_values() {
379 std::vector<int> result{};
380 for (int value = -1; value <= (int)get_maximal_threads_per_core(); ++value) {
381 if (value != 0) {
382 result.push_back(value);
383 }
384 }
385 return result;
386 }
387 }; // class system_info
388
389 system_info* system_info::system_info_ptr{nullptr};
390
prepare_reference_affinity_mask(const tbb::task_arena::constraints & c)391 system_info::affinity_mask prepare_reference_affinity_mask(const tbb::task_arena::constraints& c) {
392 auto reference_affinity = system_info::allocate_empty_affinity_mask();
393 hwloc_bitmap_copy(reference_affinity, system_info::get_process_affinity_mask());
394
395 if (c.numa_id != tbb::task_arena::automatic) {
396 const auto& numa_nodes_info = system_info::get_numa_nodes_info();
397 auto required_info = std::find_if(numa_nodes_info.begin(), numa_nodes_info.end(),
398 [&](const index_info& info) { return info.index == c.numa_id; }
399 );
400
401 REQUIRE_MESSAGE(required_info != numa_nodes_info.end(), "Constraints instance has wrong NUMA index.");
402 hwloc_bitmap_and(reference_affinity, reference_affinity, required_info->cpuset);
403 }
404
405 if (c.core_type != tbb::task_arena::automatic) {
406 const auto& core_types_info = system_info::get_cpu_kinds_info();
407 auto required_info = std::find_if(core_types_info.begin(), core_types_info.end(),
408 [&](index_info info) { return info.index == c.core_type; }
409 );
410 REQUIRE_MESSAGE(required_info != core_types_info.end(), "Constraints instance has wrong core type index.");
411 hwloc_bitmap_and(reference_affinity, reference_affinity, required_info->cpuset);
412 }
413
414 return reference_affinity;
415 }
416
test_constraints_affinity_and_concurrency(tbb::task_arena::constraints constraints,system_info::affinity_mask arena_affinity)417 void test_constraints_affinity_and_concurrency(tbb::task_arena::constraints constraints,
418 system_info::affinity_mask arena_affinity) {
419 int default_concurrency = tbb::info::default_concurrency(constraints);
420 system_info::affinity_mask reference_affinity = prepare_reference_affinity_mask(constraints);
421 int max_threads_per_core = static_cast<int>(system_info::get_maximal_threads_per_core());
422
423 if (constraints.max_threads_per_core == tbb::task_arena::automatic || constraints.max_threads_per_core == max_threads_per_core) {
424 REQUIRE_MESSAGE(hwloc_bitmap_isequal(reference_affinity, arena_affinity),
425 "Wrong affinity mask was applied for the constraints instance.");
426 REQUIRE_MESSAGE(hwloc_bitmap_weight(reference_affinity) == default_concurrency,
427 "Wrong default_concurrency was returned for the constraints instance.");
428 } else {
429 REQUIRE_MESSAGE(constraints.max_threads_per_core < max_threads_per_core,
430 "Constraints instance has wrong max_threads_per_core value.");
431 REQUIRE_MESSAGE(hwloc_bitmap_isincluded(arena_affinity, reference_affinity),
432 "If custom threads per core value is applied then the applied affinity"
433 "should be a sub-set of the affinity applied to constraints without such restriction.");
434
435 system_info::affinity_mask core_affinity = system_info::allocate_empty_affinity_mask();
436 int threads_per_current_core = 0, valid_concurrency = 0;
437 for (const auto& current_core : system_info::get_cores_info()) {
438 hwloc_bitmap_and(core_affinity, reference_affinity, current_core.cpuset);
439 threads_per_current_core = hwloc_bitmap_weight(core_affinity);
440 if (threads_per_current_core > 0) { // current core should exist in the valid affinity mask
441 hwloc_bitmap_and(core_affinity, arena_affinity, current_core.cpuset);
442 threads_per_current_core = std::min(threads_per_current_core, constraints.max_threads_per_core);
443 valid_concurrency += threads_per_current_core;
444 REQUIRE_MESSAGE(hwloc_bitmap_weight(core_affinity) == threads_per_current_core ,
445 "Wrong number of threads may be scheduled to some core.");
446 }
447 }
448 REQUIRE_MESSAGE(valid_concurrency == default_concurrency,
449 "Wrong default_concurrency was returned for the constraints instance.");
450 REQUIRE_MESSAGE(valid_concurrency == hwloc_bitmap_weight(arena_affinity),
451 "Wrong number of bits inside the affinity mask.");
452 }
453 }
454
get_arena_affinity(tbb::task_arena & ta)455 system_info::affinity_mask get_arena_affinity(tbb::task_arena& ta) {
456 system_info::affinity_mask arena_affinity;
457 ta.execute([&]{
458 arena_affinity = system_info::allocate_current_affinity_mask();
459 });
460
461 utils::SpinBarrier exit_barrier(ta.max_concurrency());
462 tbb::spin_mutex affinity_mutex{};
463 for (int i = 0; i < ta.max_concurrency() - 1; ++i) {
464 ta.enqueue([&] {
465 {
466 tbb::spin_mutex::scoped_lock lock(affinity_mutex);
467 system_info::affinity_mask thread_affinity = system_info::allocate_current_affinity_mask();
468 if (get_processors_group_count() == 1) {
469 REQUIRE_MESSAGE(hwloc_bitmap_isequal(thread_affinity, arena_affinity),
470 "Threads have different masks on machine without several processors groups.");
471 }
472 hwloc_bitmap_or(arena_affinity, arena_affinity, thread_affinity);
473 }
474 exit_barrier.wait();
475 });
476 }
477 exit_barrier.wait();
478 return arena_affinity;
479 }
480
481 #else /*__TBB_HWLOC_VALID_ENVIRONMENT*/
482
483 namespace system_info {
484 // The values that seems meaningful for the most systems that exists at this moment
485 // Should be used when we cannot parse the system topology
get_available_max_threads_values()486 std::vector<int> get_available_max_threads_values() { return {tbb::task_arena::automatic, 1, 2}; }
487 }
488
489 #endif /*!__TBB_HWLOC_VALID_ENVIRONMENT*/
490
491 struct constraints_hash {
operatorconstraints_hash492 std::size_t operator()(const tbb::task_arena::constraints& c) const {
493 return (std::hash<int>{}(c.numa_id) ^ std::hash<int>{}(c.core_type) ^ std::hash<int>{}(c.max_threads_per_core));
494 }
495 };
496
497 struct constraints_equal {
operatorconstraints_equal498 bool operator()(const tbb::task_arena::constraints& c1, const tbb::task_arena::constraints& c2) const {
499 return (c1.numa_id == c2.numa_id &&
500 c1.core_type == c2.core_type &&
501 c1.max_threads_per_core == c2.max_threads_per_core);
502 }
503 };
504
505 using constraints_container = std::unordered_set<tbb::task_arena::constraints, constraints_hash, constraints_equal>;
506
507 // Using unordered_set to get rid of duplicated elements
generate_constraints_variety()508 constraints_container generate_constraints_variety() {
509 static constraints_container constraints_variety = [](){
510 using constraints = tbb::task_arena::constraints;
511 constraints_container results{};
512
513 std::vector<tbb::numa_node_id> numa_nodes = tbb::info::numa_nodes();
514 numa_nodes.push_back((tbb::numa_node_id)tbb::task_arena::automatic);
515
516 #if __HYBRID_CPUS_TESTING
517 std::vector<tbb::core_type_id> core_types = tbb::info::core_types();
518 core_types.push_back((tbb::core_type_id)tbb::task_arena::automatic);
519 #endif
520
521 results.insert(constraints{});
522 for (const auto& numa_node : numa_nodes) {
523 results.insert(constraints{}.set_numa_id(numa_node));
524 #if __HYBRID_CPUS_TESTING
525 for (const auto& core_type : core_types) {
526 results.insert(constraints{}.set_core_type(core_type));
527
528 results.insert(
529 constraints{}
530 .set_numa_id(numa_node)
531 .set_core_type(core_type)
532 );
533
534 for (const auto& max_threads_per_core: system_info::get_available_max_threads_values()) {
535 results.insert(
536 constraints{}
537 .set_max_threads_per_core(max_threads_per_core)
538 );
539
540 results.insert(
541 constraints{}
542 .set_numa_id(numa_node)
543 .set_max_threads_per_core(max_threads_per_core)
544 );
545
546 results.insert(
547 constraints{}
548 .set_core_type(core_type)
549 .set_max_threads_per_core(max_threads_per_core)
550 );
551
552 results.insert(
553 constraints{}
554 .set_numa_id(numa_node)
555 .set_core_type(core_type)
556 .set_max_threads_per_core(max_threads_per_core)
557 );
558 }
559 }
560 #endif /*__HYBRID_CPUS_TESTING*/
561 }
562
563 // Some constraints may cause unexpected behavior, which would be fixed later.
564 if (get_processors_group_count() > 1) {
565 for(auto it = results.begin(); it != results.end(); ++it) {
566 if (it->max_threads_per_core != tbb::task_arena::automatic
567 && (it->numa_id == tbb::task_arena::automatic || tbb::info::numa_nodes().size() == 1)
568 #if __HYBRID_CPUS_TESTING
569 && (it->core_type == tbb::task_arena::automatic || tbb::info::core_types().size() == 1)
570 #endif /*__HYBRID_CPUS_TESTING*/
571 ) {
572 it = results.erase(it);
573 }
574 }
575 }
576 return results;
577 }();
578
579 return constraints_variety;
580 }
581 #endif // __TBB_test_common_arena_constraints_H_
582