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