1 /* 2 Copyright (c) 2019-2022 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 //! 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 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 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*/ 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}; 112 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; 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; 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