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