diff --git a/include/oneapi/tbb/partitioner.h b/include/oneapi/tbb/partitioner.h index f09786c022..9961ace5a3 100644 --- a/include/oneapi/tbb/partitioner.h +++ b/include/oneapi/tbb/partitioner.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2023 Intel Corporation + Copyright (c) 2005-2024 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ #include "cache_aligned_allocator.h" #include "task_group.h" // task_group_context #include "task_arena.h" +#include "global_control.h" #include #include @@ -70,7 +71,8 @@ class affinity_partitioner_base; inline std::size_t get_initial_auto_partitioner_divisor() { const std::size_t factor = 4; - return factor * static_cast(max_concurrency()); + return factor * std::min(static_cast(max_concurrency()), + tbb::global_control::active_value(tbb::global_control::max_allowed_parallelism)); } //! Defines entry point for affinity partitioner into oneTBB run-time library. @@ -90,7 +92,8 @@ class affinity_partitioner_base: no_copy { /** Retains values if resulting size is the same. */ void resize(unsigned factor) { // Check factor to avoid asking for number of workers while there might be no arena. - unsigned max_threads_in_arena = static_cast(max_concurrency()); + unsigned max_threads_in_arena = unsigned(std::min(static_cast(max_concurrency()), + tbb::global_control::active_value(tbb::global_control::max_allowed_parallelism))); std::size_t new_size = factor ? factor * max_threads_in_arena : 0; if (new_size != my_size) { if (my_array) { diff --git a/test/tbb/test_task.cpp b/test/tbb/test_task.cpp index 876e351006..e30b22888c 100644 --- a/test/tbb/test_task.cpp +++ b/test/tbb/test_task.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2023 Intel Corporation + Copyright (c) 2005-2024 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ #include "common/spin_barrier.h" #include "common/utils_concurrency_limit.h" #include "common/cpu_usertime.h" +#include "common/memory_usage.h" #include "tbb/task.h" #include "tbb/task_group.h" @@ -840,3 +841,28 @@ TEST_CASE("Check correct arena destruction with enqueue") { tbb::finalize(handle, std::nothrow_t{}); } } + +//! \brief \ref regression +TEST_CASE("Check that memory does not leak with static_partitioner + global_control") { + tbb::global_control gbl_ctrl{ tbb::global_control::max_allowed_parallelism, std::size_t(tbb::this_task_arena::max_concurrency() / 2) }; + + size_t current_memory_usage = 0, previous_memory_usage = 0, stability_counter = 0; + bool no_memory_leak = false; + std::size_t num_iterations = 100; + for (std::size_t i = 0; i < num_iterations; ++i) { + for (std::size_t j = 0; j < 100; ++j) { + tbb::parallel_for(0, 1000, [] (int) {}, tbb::static_partitioner{}); + } + + current_memory_usage = utils::GetMemoryUsage(); + stability_counter = current_memory_usage==previous_memory_usage ? stability_counter + 1 : 0; + // If the amount of used memory has not changed during 5% of executions, + // then we can assume that the check was successful + if (stability_counter > num_iterations / 20) { + no_memory_leak = true; + break; + } + previous_memory_usage = current_memory_usage; + } + REQUIRE_MESSAGE(no_memory_leak, "Seems we get memory leak here."); +}