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 //! Testing memory leaks absence
108 //! \brief \ref resource_usage
109 TEST_CASE("Test memory leaks") {
110     constexpr size_t num_trials = 1000;
111 
112     // To reduce the test session time only one constraints object is used inside this test.
113     // This constraints should use all available settings to cover the most part of tbbbind functionality.
114     auto constraints = tbb::task_arena::constraints{}
115         .set_numa_id(tbb::info::numa_nodes().front())
116         .set_core_type(tbb::info::core_types().front())
117         .set_max_threads_per_core(1);
118 
119     size_t current_memory_usage = 0, previous_memory_usage = 0, stability_counter = 0;
120     bool no_memory_leak = false;
121     for (size_t i = 0; i < num_trials; i++) {
122         { /* All DTORs must be called before GetMemoryUsage() call*/
123             tbb::task_arena arena{constraints};
124             arena.execute([]{
125                 utils::SpinBarrier barrier;
126                 barrier.initialize(tbb::this_task_arena::max_concurrency());
127                 tbb::parallel_for(
128                     tbb::blocked_range<size_t>(0, tbb::this_task_arena::max_concurrency()),
129                     [&barrier](const tbb::blocked_range<size_t>&) {
130                         barrier.wait();
131                     }
132                 );
133             });
134         }
135 
136         current_memory_usage = utils::GetMemoryUsage();
137         stability_counter = current_memory_usage==previous_memory_usage ? stability_counter + 1 : 0;
138         // If the amount of used memory has not changed during 5% of executions,
139         // then we can assume that the check was successful
140         if (stability_counter > num_trials / 20) {
141             no_memory_leak = true;
142             break;
143         }
144         previous_memory_usage = current_memory_usage;
145     }
146     REQUIRE_MESSAGE(no_memory_leak, "Seems we get memory leak here.");
147 }
148 
149 //! Testing arena constraints setters
150 //! \brief \ref interface \ref requirement
151 TEST_CASE("Test arena constraints setters") {
152     using constraints = tbb::task_arena::constraints;
153     auto constraints_comparison = [](const constraints& c1, const constraints& c2) {
154         REQUIRE_MESSAGE(constraints_equal{}(c1, c2),
155             "Equal constraints settings specified by different interfaces shows different result.");
156     };
157 
158     // NUMA node ID setter testing
159     for(const auto& numa_index: tbb::info::numa_nodes()) {
160         constraints setter_c = constraints{}.set_numa_id(numa_index);
161         constraints assignment_c{}; assignment_c.numa_id = numa_index;
162 
163         constraints_comparison(setter_c, assignment_c);
164     }
165 
166     // Core type setter testing
167     for(const auto& core_type_index: tbb::info::core_types()) {
168         constraints setter_c = constraints{}.set_core_type(core_type_index);
169         constraints assignment_c{}; assignment_c.core_type = core_type_index;
170 
171         constraints_comparison(setter_c, assignment_c);
172     }
173 
174     // Max concurrency setter testing
175     {
176         constraints setter_c = constraints{}.set_max_concurrency(1);
177         constraints assignment_c{}; assignment_c.max_concurrency = 1;
178 
179         constraints_comparison(setter_c, assignment_c);
180     }
181 
182     // Threads per core setter testing
183     {
184         constraints setter_c = constraints{}.set_max_threads_per_core(1);
185         constraints assignment_c{}; assignment_c.max_threads_per_core = 1;
186 
187         constraints_comparison(setter_c, assignment_c);
188     }
189 }
190 
191 const int custom_concurrency_value = 42;
192 void check_concurrency_level(const tbb::task_arena::constraints& c) {
193     REQUIRE_MESSAGE(tbb::info::default_concurrency(c) == custom_concurrency_value,
194         "Custom arena concurrency was passed to constraints, but was not respected by default_concurrency() call.");
195     REQUIRE_MESSAGE(tbb::task_arena{c}.max_concurrency() == custom_concurrency_value,
196         "Custom arena concurrency was passed to constraints, but was not respected by default_concurrency() call.");
197 }
198 
199 //! Testing concurrency getters output for constraints with custom concurrency value
200 //! \brief \ref interface \ref error_guessing
201 TEST_CASE("Test concurrency getters output for constraints with custom concurrency value") {
202     tbb::task_arena::constraints c{};
203     c.set_max_concurrency(custom_concurrency_value);
204     check_concurrency_level(c);
205 
206     c.set_numa_id(tbb::info::numa_nodes().front());
207     check_concurrency_level(c);
208 
209     c.set_core_type(tbb::info::core_types().front());
210     check_concurrency_level(c);
211 
212     c.set_max_threads_per_core(1);
213     check_concurrency_level(c);
214 }
215 
216 //! Testing constraints_threads_per_core() reserved entry point
217 //! \brief \ref error_guessing
218 TEST_CASE("Testing constraints_threads_per_core() reserved entry point") {
219     tbb::task_arena::constraints c{};
220     tbb::detail::r1::constraints_threads_per_core(c);
221 }
222