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 //! 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