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 //! \file test_arena_constraints.cpp
18 //! \brief Test for [info_namespace scheduler.task_arena] specifications
19 
20 #include "common/common_arena_constraints.h"
21 
22 #include "tbb/parallel_for.h"
23 
24 #if __TBB_HWLOC_VALID_ENVIRONMENT && __HWLOC_CPUBIND_PRESENT
25 //! Test affinity and default_concurrency correctness for all available constraints.
26 //! \brief \ref error_guessing
27 TEST_CASE("Test affinity and default_concurrency correctness for all available constraints.") {
28     system_info::initialize();
29     for (const auto& constraints: generate_constraints_variety()) {
30         tbb::task_arena ta{constraints};
31         test_constraints_affinity_and_concurrency(constraints, get_arena_affinity(ta));
32     }
33 }
34 
is_observer_created(const tbb::task_arena::constraints & c)35 bool is_observer_created(const tbb::task_arena::constraints& c) {
36     std::vector<tbb::core_type_id> core_types = tbb::info::core_types();
37     std::vector<tbb::numa_node_id> numa_nodes = tbb::info::numa_nodes();
38     return
39         (c.numa_id != tbb::task_arena::automatic && numa_nodes.size() > 1) ||
40         (c.core_type != tbb::task_arena::automatic && core_types.size() > 1) ||
41         c.max_threads_per_core != tbb::task_arena::automatic;
42 }
43 
recursive_arena_binding(constraints_container::iterator current_pos,constraints_container::iterator end_pos)44 void recursive_arena_binding(constraints_container::iterator current_pos, constraints_container::iterator end_pos) {
45     system_info::affinity_mask affinity_before = system_info::allocate_current_affinity_mask();
46 
47     if (current_pos != end_pos) {
48         auto constraints = *current_pos;
49         tbb::task_arena current_level_arena{constraints};
50 
51         if (is_observer_created(constraints)) {
52             system_info::affinity_mask affinity = get_arena_affinity(current_level_arena);
53             test_constraints_affinity_and_concurrency(constraints, affinity);
54         }
55 
56         current_level_arena.execute(
57             [&current_pos, &end_pos]() {
58                 recursive_arena_binding(++current_pos, end_pos);
59             }
60         );
61     }
62 
63     system_info::affinity_mask affinity_after = system_info::allocate_current_affinity_mask();
64     REQUIRE_MESSAGE(hwloc_bitmap_isequal(affinity_before, affinity_after),
65         "After nested arena execution previous affinity mask was not restored.");
66 }
67 
68 //! Testing binding correctness during passing through nested arenas
69 //! \brief \ref interface \ref error_guessing
70 TEST_CASE("Test binding with nested arenas") {
71     system_info::initialize();
72     auto constraints_variety = generate_constraints_variety();
73     recursive_arena_binding(constraints_variety.begin(), constraints_variety.end());
74 }
75 
76 
77 //! Testing constraints propagation during arenas copy construction
78 //! \brief \ref regression
79 TEST_CASE("Test constraints propagation during arenas copy construction") {
80     system_info::initialize();
81     for (const auto& constraints: generate_constraints_variety()) {
82         tbb::task_arena constructed{constraints};
83 
84         tbb::task_arena copied(constructed);
85         system_info::affinity_mask copied_affinity = get_arena_affinity(copied);
86 
87         test_constraints_affinity_and_concurrency(constraints, copied_affinity);
88     }
89 }
90 #endif /*__TBB_HWLOC_VALID_ENVIRONMENT && __HWLOC_CPUBIND_PRESENT */
91 
92 // The test cannot be stabilized with TBB malloc under Thread Sanitizer
93 #if !__TBB_USE_THREAD_SANITIZER
94 
95 //! Testing memory leaks absence
96 //! \brief \ref resource_usage
97 TEST_CASE("Test memory leaks") {
98     constexpr size_t num_trials = 1000;
99 
100     // To reduce the test session time only one constraints object is used inside this test.
101     // This constraints should use all available settings to cover the most part of tbbbind functionality.
102     auto constraints = tbb::task_arena::constraints{}
103         .set_numa_id(tbb::info::numa_nodes().front())
104         .set_core_type(tbb::info::core_types().front())
105         .set_max_threads_per_core(1);
106 
107     size_t current_memory_usage = 0, previous_memory_usage = 0, stability_counter = 0;
108     bool no_memory_leak = false;
109     for (size_t i = 0; i < num_trials; i++) {
110         { /* All DTORs must be called before GetMemoryUsage() call*/
111             tbb::task_arena arena{constraints};
__anon2861f8e80202null112             arena.execute([]{
113                 utils::SpinBarrier barrier;
114                 barrier.initialize(tbb::this_task_arena::max_concurrency());
115                 tbb::parallel_for(
116                     tbb::blocked_range<size_t>(0, tbb::this_task_arena::max_concurrency()),
117                     [&barrier](const tbb::blocked_range<size_t>&) {
118                         barrier.wait();
119                     }
120                 );
121             });
122         }
123 
124         current_memory_usage = utils::GetMemoryUsage();
125         stability_counter = current_memory_usage==previous_memory_usage ? stability_counter + 1 : 0;
126         // If the amount of used memory has not changed during 5% of executions,
127         // then we can assume that the check was successful
128         if (stability_counter > num_trials / 20) {
129             no_memory_leak = true;
130             break;
131         }
132         previous_memory_usage = current_memory_usage;
133     }
134     REQUIRE_MESSAGE(no_memory_leak, "Seems we get memory leak here.");
135 }
136 #endif
137 
138 //! Testing arena constraints setters
139 //! \brief \ref interface \ref requirement
140 TEST_CASE("Test arena constraints setters") {
141     using constraints = tbb::task_arena::constraints;
__anon2861f8e80402(const constraints& c1, const constraints& c2) 142     auto constraints_comparison = [](const constraints& c1, const constraints& c2) {
143         REQUIRE_MESSAGE(constraints_equal{}(c1, c2),
144             "Equal constraints settings specified by different interfaces shows different result.");
145     };
146 
147     // NUMA node ID setter testing
148     for(const auto& numa_index: tbb::info::numa_nodes()) {
149         constraints setter_c = constraints{}.set_numa_id(numa_index);
150         constraints assignment_c{}; assignment_c.numa_id = numa_index;
151 
152         constraints_comparison(setter_c, assignment_c);
153     }
154 
155     // Core type setter testing
156     for(const auto& core_type_index: tbb::info::core_types()) {
157         constraints setter_c = constraints{}.set_core_type(core_type_index);
158         constraints assignment_c{}; assignment_c.core_type = core_type_index;
159 
160         constraints_comparison(setter_c, assignment_c);
161     }
162 
163     // Max concurrency setter testing
164     {
165         constraints setter_c = constraints{}.set_max_concurrency(1);
166         constraints assignment_c{}; assignment_c.max_concurrency = 1;
167 
168         constraints_comparison(setter_c, assignment_c);
169     }
170 
171     // Threads per core setter testing
172     {
173         constraints setter_c = constraints{}.set_max_threads_per_core(1);
174         constraints assignment_c{}; assignment_c.max_threads_per_core = 1;
175 
176         constraints_comparison(setter_c, assignment_c);
177     }
178 }
179 
180 const int custom_concurrency_value = 42;
check_concurrency_level(const tbb::task_arena::constraints & c)181 void check_concurrency_level(const tbb::task_arena::constraints& c) {
182     REQUIRE_MESSAGE(tbb::info::default_concurrency(c) == custom_concurrency_value,
183         "Custom arena concurrency was passed to constraints, but was not respected by default_concurrency() call.");
184     REQUIRE_MESSAGE(tbb::task_arena{c}.max_concurrency() == custom_concurrency_value,
185         "Custom arena concurrency was passed to constraints, but was not respected by default_concurrency() call.");
186 }
187 
188 //! Testing concurrency getters output for constraints with custom concurrency value
189 //! \brief \ref interface \ref error_guessing
190 TEST_CASE("Test concurrency getters output for constraints with custom concurrency value") {
191     tbb::task_arena::constraints c{};
192     c.set_max_concurrency(custom_concurrency_value);
193     check_concurrency_level(c);
194 
195     c.set_numa_id(tbb::info::numa_nodes().front());
196     check_concurrency_level(c);
197 
198     c.set_core_type(tbb::info::core_types().front());
199     check_concurrency_level(c);
200 
201     c.set_max_threads_per_core(1);
202     check_concurrency_level(c);
203 }
204 
205 //! Testing constraints_threads_per_core() reserved entry point
206 //! \brief \ref error_guessing
207 TEST_CASE("Testing constraints_threads_per_core() reserved entry point") {
208     tbb::task_arena::constraints c{};
209     tbb::detail::r1::constraints_threads_per_core(c);
210 }
211