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 [¤t_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