From 5e5778e7baa198be6619576420b77fc27b945ddb Mon Sep 17 00:00:00 2001 From: pavelkumbrasev Date: Wed, 13 Nov 2024 10:56:04 +0000 Subject: [PATCH 01/35] Add initial impl of Parallel Block Signed-off-by: pavelkumbrasev --- include/oneapi/tbb/detail/_config.h | 4 ++ include/oneapi/tbb/task_arena.h | 101 +++++++++++++++++++++++----- src/tbb/arena.cpp | 45 +++++++++++-- src/tbb/arena.h | 86 ++++++++++++++++++++++- src/tbb/waiters.h | 14 +++- test/common/config.h | 3 + test/tbb/test_task_arena.cpp | 38 +++++++++++ 7 files changed, 264 insertions(+), 27 deletions(-) diff --git a/include/oneapi/tbb/detail/_config.h b/include/oneapi/tbb/detail/_config.h index e676b1558b..bf2c085609 100644 --- a/include/oneapi/tbb/detail/_config.h +++ b/include/oneapi/tbb/detail/_config.h @@ -534,4 +534,8 @@ #define __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #endif +#if TBB_PREVIEW_PARALLEL_BLOCK || __TBB_BUILD +#define __TBB_PREVIEW_PARALLEL_BLOCK 1 +#endif + #endif // __TBB_detail__config_H diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 5ce41d99c9..17f7fdb4a0 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -122,6 +122,14 @@ class task_arena_base { normal = 2 * priority_stride, high = 3 * priority_stride }; + +#if __TBB_PREVIEW_PARALLEL_BLOCK + enum class workers_leave : int { + fast = 1, + delayed = 2 + }; +#endif + #if __TBB_ARENA_BINDING using constraints = tbb::detail::d1::constraints; #endif /*__TBB_ARENA_BINDING*/ @@ -154,6 +162,11 @@ class task_arena_base { //! Number of threads per core int my_max_threads_per_core; +#if __TBB_PREVIEW_PARALLEL_BLOCK + //! Defines the initial behavior of the workers_leave state machine + workers_leave my_workers_leave; +#endif + // Backward compatibility checks. core_type_id core_type() const { return (my_version_and_traits & core_type_support_flag) == core_type_support_flag ? my_core_type : automatic; @@ -167,7 +180,11 @@ class task_arena_base { , core_type_support_flag = 1 }; - task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority) + task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl +#endif + ) : my_version_and_traits(default_flags | core_type_support_flag) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) @@ -177,10 +194,17 @@ class task_arena_base { , my_numa_id(automatic) , my_core_type(automatic) , my_max_threads_per_core(automatic) +#if __TBB_PREVIEW_PARALLEL_BLOCK + , my_workers_leave(wl) +#endif {} #if __TBB_ARENA_BINDING - task_arena_base(const constraints& constraints_, unsigned reserved_for_masters, priority a_priority) + task_arena_base(const constraints& constraints_, unsigned reserved_for_masters, priority a_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl +#endif + ) : my_version_and_traits(default_flags | core_type_support_flag) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) @@ -190,6 +214,9 @@ class task_arena_base { , my_numa_id(constraints_.numa_id) , my_core_type(constraints_.core_type) , my_max_threads_per_core(constraints_.max_threads_per_core) +#if __TBB_PREVIEW_PARALLEL_BLOCK + , my_workers_leave(wl) +#endif {} #endif /*__TBB_ARENA_BINDING*/ public: @@ -259,31 +286,55 @@ class task_arena : public task_arena_base { * Value of 1 is default and reflects behavior of implicit arenas. **/ task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - : task_arena_base(max_concurrency_, reserved_for_masters, a_priority) + priority a_priority = priority::normal +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl = workers_leave::delayed +#endif + ) + : task_arena_base(max_concurrency_, reserved_for_masters, a_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , wl +#endif + ) {} #if __TBB_ARENA_BINDING //! Creates task arena pinned to certain NUMA node task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - : task_arena_base(constraints_, reserved_for_masters, a_priority) + priority a_priority = priority::normal +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl = workers_leave::delayed +#endif + ) + : task_arena_base(constraints_, reserved_for_masters, a_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , wl +#endif + ) {} //! Copies settings from another task_arena - task_arena(const task_arena &s) // copy settings but not the reference or instance + task_arena(const task_arena &a) // copy settings but not the reference or instance : task_arena_base( constraints{} - .set_numa_id(s.my_numa_id) - .set_max_concurrency(s.my_max_concurrency) - .set_core_type(s.my_core_type) - .set_max_threads_per_core(s.my_max_threads_per_core) - , s.my_num_reserved_slots, s.my_priority) + .set_numa_id(a.my_numa_id) + .set_max_concurrency(a.my_max_concurrency) + .set_core_type(a.my_core_type) + .set_max_threads_per_core(a.my_max_threads_per_core) + , a.my_num_reserved_slots, a.my_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , a.my_workers_leave +#endif + ) {} #else //! Copies settings from another task_arena task_arena(const task_arena& a) // copy settings but not the reference or instance - : task_arena_base(a.my_max_concurrency, a.my_num_reserved_slots, a.my_priority) + : task_arena_base(a.my_max_concurrency, a.my_num_reserved_slots, a.my_priority +#if __TBB_PREVIEW_PARALLEL_BLOCK + , a.my_workers_leave +#endif + ) {} #endif /*__TBB_ARENA_BINDING*/ @@ -292,7 +343,11 @@ class task_arena : public task_arena_base { //! Creates an instance of task_arena attached to the current arena of the thread explicit task_arena( attach ) - : task_arena_base(automatic, 1, priority::normal) // use default settings if attach fails + : task_arena_base(automatic, 1, priority::normal +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave::delayed +#endif + ) // use default settings if attach fails { if (r1::attach(*this)) { mark_initialized(); @@ -311,13 +366,20 @@ class task_arena : public task_arena_base { //! Overrides concurrency level and forces initialization of internal representation void initialize(int max_concurrency_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) + priority a_priority = priority::normal +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl = workers_leave::delayed +#endif + ) { __TBB_ASSERT(!my_arena.load(std::memory_order_relaxed), "Impossible to modify settings of an already initialized task_arena"); if( !is_active() ) { my_max_concurrency = max_concurrency_; my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; +#if __TBB_PREVIEW_PARALLEL_BLOCK + my_workers_leave = wl; +#endif r1::initialize(*this); mark_initialized(); } @@ -325,7 +387,11 @@ class task_arena : public task_arena_base { #if __TBB_ARENA_BINDING void initialize(constraints constraints_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) + priority a_priority = priority::normal +#if __TBB_PREVIEW_PARALLEL_BLOCK + , workers_leave wl = workers_leave::delayed +#endif + ) { __TBB_ASSERT(!my_arena.load(std::memory_order_relaxed), "Impossible to modify settings of an already initialized task_arena"); if( !is_active() ) { @@ -335,6 +401,9 @@ class task_arena : public task_arena_base { my_max_threads_per_core = constraints_.max_threads_per_core; my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; +#if __TBB_PREVIEW_PARALLEL_BLOCK + my_workers_leave = wl; +#endif r1::initialize(*this); mark_initialized(); } diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 6ca062d02f..ae8113a139 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -241,7 +241,12 @@ void arena::process(thread_data& tls) { __TBB_ASSERT(tls.my_arena == this, "my_arena is used as a hint when searching the arena to join"); } -arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level) { +arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl +#endif +) +{ __TBB_ASSERT( !my_guard, "improperly allocated arena?" ); __TBB_ASSERT( sizeof(my_slots[0]) % cache_line_size()==0, "arena::slot size not multiple of cache line size" ); __TBB_ASSERT( is_aligned(this, cache_line_size()), "arena misaligned" ); @@ -276,10 +281,18 @@ arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserv my_critical_task_stream.initialize(my_num_slots); #endif my_mandatory_requests = 0; + +#if __TBB_PREVIEW_PARALLEL_BLOCK + my_thread_leave.set_initial_state(wl); +#endif } arena& arena::allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, - unsigned priority_level) + unsigned priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl +#endif +) { __TBB_ASSERT( sizeof(base_type) + sizeof(arena_slot) == sizeof(arena), "All arena data fields must go to arena_base" ); __TBB_ASSERT( sizeof(base_type) % cache_line_size() == 0, "arena slots area misaligned: wrong padding" ); @@ -290,7 +303,11 @@ arena& arena::allocate_arena(threading_control* control, unsigned num_slots, uns std::memset( storage, 0, n ); return *new( storage + num_arena_slots(num_slots, num_reserved_slots) * sizeof(mail_outbox) ) - arena(control, num_slots, num_reserved_slots, priority_level); + arena(control, num_slots, num_reserved_slots, priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , wl +#endif + ); } void arena::free_arena () { @@ -340,6 +357,9 @@ bool arena::has_enqueued_tasks() { } void arena::request_workers(int mandatory_delta, int workers_delta, bool wakeup_threads) { +#if __TBB_PREVIEW_PARALLEL_BLOCK + my_thread_leave.restore_state_if_needed(); +#endif my_threading_control->adjust_demand(my_tc_client, mandatory_delta, workers_delta); if (wakeup_threads) { @@ -443,11 +463,20 @@ void arena::enqueue_task(d1::task& t, d1::task_group_context& ctx, thread_data& advertise_new_work(); } -arena& arena::create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints) { +arena& arena::create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl +#endif +) +{ __TBB_ASSERT(num_slots > 0, NULL); __TBB_ASSERT(num_reserved_slots <= num_slots, NULL); // Add public market reference for an external thread/task_arena (that adds an internal reference in exchange). - arena& a = arena::allocate_arena(control, num_slots, num_reserved_slots, arena_priority_level); + arena& a = arena::allocate_arena(control, num_slots, num_reserved_slots, arena_priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , wl +#endif + ); a.my_tc_client = control->create_client(a); // We should not publish arena until all fields are initialized control->publish_client(a.my_tc_client, constraints); @@ -568,7 +597,11 @@ void task_arena_impl::initialize(d1::task_arena_base& ta) { __TBB_ASSERT(ta.my_arena.load(std::memory_order_relaxed) == nullptr, "Arena already initialized"); unsigned priority_level = arena_priority_level(ta.my_priority); threading_control* thr_control = threading_control::register_public_reference(); - arena& a = arena::create(thr_control, unsigned(ta.my_max_concurrency), ta.my_num_reserved_slots, priority_level, arena_constraints); + arena& a = arena::create(thr_control, unsigned(ta.my_max_concurrency), ta.my_num_reserved_slots, priority_level, arena_constraints +#if __TBB_PREVIEW_PARALLEL_BLOCK + , ta.my_workers_leave +#endif + ); ta.my_arena.store(&a, std::memory_order_release); #if __TBB_CPUBIND_PRESENT diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 1e95f117b2..d28ce3f354 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -179,6 +179,69 @@ class atomic_flag { } }; +#if __TBB_PREVIEW_PARALLEL_BLOCK +class thread_leave_manager { + static const std::uintptr_t FAST_LEAVE = 1; + static const std::uintptr_t ONE_TIME_FAST_LEAVE = 1 << 1; + static const std::uintptr_t DELAYED_LEAVE = 1 << 2; + static const std::uintptr_t PARALLEL_BLOCK = 1 << 3; + + std::atomic my_state{0}; +public: + void set_initial_state(tbb::task_arena::workers_leave wl) { + if (wl == tbb::task_arena::workers_leave::delayed) { + my_state.store(DELAYED_LEAVE, std::memory_order_relaxed); + } else { + my_state.store(FAST_LEAVE, std::memory_order_relaxed); + } + } + + void restore_state_if_needed() { + std::uintptr_t curr = ONE_TIME_FAST_LEAVE; + if (my_state.load(std::memory_order_relaxed) == curr) { + my_state.compare_exchange_strong(curr, DELAYED_LEAVE); + } + } + + void register_parallel_block() { + __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); + + std::uintptr_t prev = my_state.load(std::memory_order_relaxed); + std::uintptr_t desired{}; + do { + if (prev & PARALLEL_BLOCK) { + desired = PARALLEL_BLOCK + prev; + } else if (prev == ONE_TIME_FAST_LEAVE) { + desired = PARALLEL_BLOCK | DELAYED_LEAVE; + } else { + desired = PARALLEL_BLOCK | prev; + } + } while (!my_state.compare_exchange_strong(prev, desired)); + } + + void register_parallel_block(bool enable_fast_leave) { + __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); + + std::uintptr_t prev = my_state.load(std::memory_order_relaxed); + std::uintptr_t desired{}; + do { + if (((prev - PARALLEL_BLOCK) & PARALLEL_BLOCK) != 0) { + desired = PARALLEL_BLOCK - prev; + } else { + desired = enable_fast_leave ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_BLOCK; + } + } while (!my_state.compare_exchange_strong(prev, desired)); + } + + bool should_leave() { + __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); + + std::uintptr_t curr = my_state.load(std::memory_order_relaxed); + return curr == FAST_LEAVE || curr == ONE_TIME_FAST_LEAVE; + } +}; +#endif /* __TBB_PREVIEW_PARALLEL_BLOCK */ + //! The structure of an arena, except the array of slots. /** Separated in order to simplify padding. Intrusive list node base class is used by market to form a list of arenas. **/ @@ -245,6 +308,11 @@ struct arena_base : padded { //! Waiting object for external threads that cannot join the arena. concurrent_monitor my_exit_monitors; +#if __TBB_PREVIEW_PARALLEL_BLOCK + //! Manages state of thread_leave state machine + thread_leave_manager my_thread_leave; +#endif + //! Coroutines (task_dispathers) cache buffer arena_co_cache my_co_cache; @@ -281,13 +349,25 @@ class arena: public padded }; //! Constructor - arena(threading_control* control, unsigned max_num_workers, unsigned num_reserved_slots, unsigned priority_level); + arena(threading_control* control, unsigned max_num_workers, unsigned num_reserved_slots, unsigned priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl +#endif + ); //! Allocate an instance of arena. static arena& allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, - unsigned priority_level); + unsigned priority_level +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl +#endif + ); - static arena& create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints = d1::constraints{}); + static arena& create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints = d1::constraints{} +#if __TBB_PREVIEW_PARALLEL_BLOCK + , tbb::task_arena::workers_leave wl = tbb::task_arena::workers_leave::delayed +#endif + ); static int unsigned num_arena_slots ( unsigned num_slots, unsigned num_reserved_slots ) { return num_reserved_slots == 0 ? num_slots : max(2u, num_slots); diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index 8ed431f857..c27a6a4749 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -58,7 +58,12 @@ class outermost_worker_waiter : public waiter_base { __TBB_ASSERT(t == nullptr, nullptr); if (is_worker_should_leave(slot)) { - if (!governor::hybrid_cpu()) { + if (!governor::hybrid_cpu() +#if __TBB_PREVIEW_PARALLEL_BLOCK + && !my_arena.my_thread_leave.should_leave() +#endif + ) + { static constexpr std::chrono::microseconds worker_wait_leave_duration(1000); static_assert(worker_wait_leave_duration > std::chrono::steady_clock::duration(1), "Clock resolution is not enough for measured interval."); @@ -70,7 +75,12 @@ class outermost_worker_waiter : public waiter_base { return true; } - if (my_arena.my_threading_control->is_any_other_client_active()) { + if (my_arena.my_threading_control->is_any_other_client_active() +#if __TBB_PREVIEW_PARALLEL_BLOCK + || my_arena.my_thread_leave.should_leave() +#endif + ) + { break; } d0::yield(); diff --git a/test/common/config.h b/test/common/config.h index c7ff8ba63a..c83fbf2f40 100644 --- a/test/common/config.h +++ b/test/common/config.h @@ -39,6 +39,9 @@ #ifndef TBB_PREVIEW_ISOLATED_TASK_GROUP #define TBB_PREVIEW_ISOLATED_TASK_GROUP 1 #endif +#ifndef TBB_PREVIEW_PARALLEL_BLOCK +#define TBB_PREVIEW_PARALLEL_BLOCK 1 +#endif #endif #include "oneapi/tbb/detail/_config.h" diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 6bd93d4c0e..8f6d424089 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2068,3 +2068,41 @@ TEST_CASE("worker threads occupy slots in correct range") { while (counter < 42) { utils::yield(); } } + +#if TBB_PREVIEW_PARALLEL_BLOCK + +//! \brief \ref interface \ref requirement +TEST_CASE("Check that leave faster with workers_leave::fast") { + std::vector start_times_for_delayed_leave; + std::vector start_times_for_fast_leave; + + std::vector start_times(utils::get_platform_max_threads()); + utils::SpinBarrier barrier(utils::get_platform_max_threads()); + auto measure_start_time = [&] { + start_times[tbb::this_task_arena::current_thread_index()] = std::chrono::steady_clock::now(); + barrier.wait(); + }; + + auto get_longest_start = [&] (std::chrono::steady_clock::time_point start_time) { + std::size_t longest_time = 0; + for (auto& time : start_times) { + longest_time = std::max(longest_time, std::chrono::duration_cast(time - start_time).count()); + } + return longest_time; + }; + + // Measure delayed leave + { + tbb::task_arena a(utils::get_platform_max_threads(), 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + + for (int i = 0; i < 1000; ++i) { + a.execute([] { + auto start_time = std::chono::steady_clock::now(); + tbb::parallel_for(0, utils::get_platform_max_threads(), measure_start_time, tbb::static_partitioner{}); + }); + start_times_for_delayed_leave.push_back(get_longest_start(start_time)); + } + } +} + +#endif From 55fd835ab7f2964e837d51a17ebd0dcb6f779aea Mon Sep 17 00:00:00 2001 From: pavelkumbrasev Date: Mon, 18 Nov 2024 15:56:52 +0000 Subject: [PATCH 02/35] Add test for workers_leave Signed-off-by: pavelkumbrasev --- src/tbb/arena.h | 2 +- test/tbb/test_task_arena.cpp | 45 +++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index d28ce3f354..fcb0d089e6 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -219,7 +219,7 @@ class thread_leave_manager { } while (!my_state.compare_exchange_strong(prev, desired)); } - void register_parallel_block(bool enable_fast_leave) { + void unregister_parallel_block(bool enable_fast_leave) { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); std::uintptr_t prev = my_state.load(std::memory_order_relaxed); diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 8f6d424089..b9e0dbfef6 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2073,12 +2073,16 @@ TEST_CASE("worker threads occupy slots in correct range") { //! \brief \ref interface \ref requirement TEST_CASE("Check that leave faster with workers_leave::fast") { + std::size_t num_threads = utils::get_platform_max_threads(); + std::size_t num_runs = 1000; std::vector start_times_for_delayed_leave; + start_times_for_delayed_leave.reserve(num_runs); std::vector start_times_for_fast_leave; + start_times_for_fast_leave.reserve(num_runs); - std::vector start_times(utils::get_platform_max_threads()); - utils::SpinBarrier barrier(utils::get_platform_max_threads()); - auto measure_start_time = [&] { + std::vector start_times(num_threads); + utils::SpinBarrier barrier(num_threads); + auto measure_start_time = [&] (std::size_t) { start_times[tbb::this_task_arena::current_thread_index()] = std::chrono::steady_clock::now(); barrier.wait(); }; @@ -2086,23 +2090,42 @@ TEST_CASE("Check that leave faster with workers_leave::fast") { auto get_longest_start = [&] (std::chrono::steady_clock::time_point start_time) { std::size_t longest_time = 0; for (auto& time : start_times) { - longest_time = std::max(longest_time, std::chrono::duration_cast(time - start_time).count()); + longest_time = std::max(longest_time, (std::size_t)std::chrono::duration_cast(time - start_time).count()); } return longest_time; }; - // Measure delayed leave + std::size_t avg_start_time_delayed = 0; + { + tbb::task_arena a(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + + for (std::size_t i = 0; i < num_runs; ++i) { + a.execute([&] { + auto start_time = std::chrono::steady_clock::now(); + tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + start_times_for_delayed_leave.push_back(get_longest_start(start_time)); + }); + std::this_thread::sleep_for(std::chrono::microseconds(i)); + } + avg_start_time_delayed = std::accumulate(start_times_for_delayed_leave.begin(), start_times_for_delayed_leave.end(), 0) / num_runs; + } + + std::size_t avg_start_time_fast = 0; { - tbb::task_arena a(utils::get_platform_max_threads(), 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena a(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); - for (int i = 0; i < 1000; ++i) { - a.execute([] { - auto start_time = std::chono::steady_clock::now(); - tbb::parallel_for(0, utils::get_platform_max_threads(), measure_start_time, tbb::static_partitioner{}); + for (std::size_t i = 0; i < num_runs; ++i) { + a.execute([&] { + auto start_time = std::chrono::steady_clock::now(); + tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + start_times_for_fast_leave.push_back(get_longest_start(start_time)); }); - start_times_for_delayed_leave.push_back(get_longest_start(start_time)); + std::this_thread::sleep_for(std::chrono::microseconds(i)); } + avg_start_time_fast = std::accumulate(start_times_for_fast_leave.begin(), start_times_for_fast_leave.end(), 0) / num_runs; } + + REQUIRE(avg_start_time_delayed < avg_start_time_fast); } #endif From 637a74d0138dd5a39cbb539dbca701ae8f47cac6 Mon Sep 17 00:00:00 2001 From: pavelkumbrasev Date: Wed, 20 Nov 2024 14:35:59 +0000 Subject: [PATCH 03/35] Fix thread_leave_manager Signed-off-by: pavelkumbrasev --- src/tbb/arena.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index fcb0d089e6..cb921fab6a 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -185,6 +185,7 @@ class thread_leave_manager { static const std::uintptr_t ONE_TIME_FAST_LEAVE = 1 << 1; static const std::uintptr_t DELAYED_LEAVE = 1 << 2; static const std::uintptr_t PARALLEL_BLOCK = 1 << 3; + static const std::uintptr_t PARALLEL_BLOCK_MASK = ~((1LLU << 32) - 1) & ~(0x7); std::atomic my_state{0}; public: @@ -199,6 +200,8 @@ class thread_leave_manager { void restore_state_if_needed() { std::uintptr_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { + // Potentially can override desicion of the parallel block from future epoch + // but it is not a problem because it does not violate the correctness my_state.compare_exchange_strong(curr, DELAYED_LEAVE); } } @@ -209,7 +212,7 @@ class thread_leave_manager { std::uintptr_t prev = my_state.load(std::memory_order_relaxed); std::uintptr_t desired{}; do { - if (prev & PARALLEL_BLOCK) { + if (prev & PARALLEL_BLOCK_MASK) { desired = PARALLEL_BLOCK + prev; } else if (prev == ONE_TIME_FAST_LEAVE) { desired = PARALLEL_BLOCK | DELAYED_LEAVE; @@ -225,10 +228,10 @@ class thread_leave_manager { std::uintptr_t prev = my_state.load(std::memory_order_relaxed); std::uintptr_t desired{}; do { - if (((prev - PARALLEL_BLOCK) & PARALLEL_BLOCK) != 0) { - desired = PARALLEL_BLOCK - prev; + if (((prev - PARALLEL_BLOCK) & PARALLEL_BLOCK_MASK) != 0) { + desired = prev - PARALLEL_BLOCK; } else { - desired = enable_fast_leave ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_BLOCK; + desired = enable_fast_leave && (prev - PARALLEL_BLOCK == DELAYED_LEAVE) ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_BLOCK; } } while (!my_state.compare_exchange_strong(prev, desired)); } From eb172a7817c7aeb81b3549b3b0bcc92c0da65bff Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 20 Nov 2024 17:15:00 +0100 Subject: [PATCH 04/35] Add parallel_block API, improve tests Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 17 ++++++ src/tbb/arena.cpp | 20 ++++++++ src/tbb/def/lin64-tbb.def | 2 + test/tbb/test_task_arena.cpp | 91 +++++++++++++++++++++------------ 4 files changed, 98 insertions(+), 32 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 17f7fdb4a0..11c3e6dca3 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -95,6 +95,11 @@ TBB_EXPORT void __TBB_EXPORTED_FUNC isolate_within_arena(d1::delegate_base& d, s TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_arena_base*); TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_group_context&, d1::task_arena_base*); TBB_EXPORT void __TBB_EXPORTED_FUNC submit(d1::task&, d1::task_group_context&, arena*, std::uintptr_t); + +#if __TBB_PREVIEW_PARALLEL_BLOCK +TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_block(d1::task_arena_base&); +TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_block(d1::task_arena_base&, bool); +#endif } // namespace r1 namespace d2 { @@ -473,6 +478,18 @@ class task_arena : public task_arena_base { return execute_impl(f); } +#if __TBB_PREVIEW_PARALLEL_BLOCK + void parallel_block_start() { + initialize(); + r1::register_parallel_block(*this); + enqueue([]{}); + } + void parallel_block_end(bool set_one_time_fast_leave = false) { + __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); + r1::unregister_parallel_block(*this, set_one_time_fast_leave); + } +#endif + #if __TBB_EXTRA_DEBUG //! Returns my_num_reserved_slots int debug_reserved_slots() const { diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index ae8113a139..2b267a3682 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -529,6 +529,8 @@ struct task_arena_impl { static int max_concurrency(const d1::task_arena_base*); static void enqueue(d1::task&, d1::task_group_context*, d1::task_arena_base*); static d1::slot_id execution_slot(const d1::task_arena_base&); + static void register_parallel_block(d1::task_arena_base&); + static void unregister_parallel_block(d1::task_arena_base&, bool); }; void __TBB_EXPORTED_FUNC initialize(d1::task_arena_base& ta) { @@ -563,6 +565,14 @@ d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::task_arena_base& arena) return task_arena_impl::execution_slot(arena); } +void __TBB_EXPORTED_FUNC register_parallel_block(d1::task_arena_base& ta) { + task_arena_impl::register_parallel_block(ta); +} + +void __TBB_EXPORTED_FUNC unregister_parallel_block(d1::task_arena_base& ta, bool one_time_fast_leave) { + task_arena_impl::unregister_parallel_block(ta, one_time_fast_leave); +} + void task_arena_impl::initialize(d1::task_arena_base& ta) { // Enforce global market initialization to properly initialize soft limit (void)governor::get_thread_data(); @@ -908,6 +918,16 @@ int task_arena_impl::max_concurrency(const d1::task_arena_base *ta) { return int(governor::default_num_threads()); } +#if __TBB_PREVIEW_PARALLEL_BLOCK +void task_arena_impl::register_parallel_block(d1::task_arena_base& ta) { + ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.register_parallel_block(); +} + +void task_arena_impl::unregister_parallel_block(d1::task_arena_base& ta, bool one_time_fast_leave) { + ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.unregister_parallel_block(one_time_fast_leave); +} +#endif + void isolate_within_arena(d1::delegate_base& d, std::intptr_t isolation) { // TODO: Decide what to do if the scheduler is not initialized. Is there a use case for it? thread_data* tls = governor::get_thread_data(); diff --git a/src/tbb/def/lin64-tbb.def b/src/tbb/def/lin64-tbb.def index 41aca2e932..afb9c2e8e0 100644 --- a/src/tbb/def/lin64-tbb.def +++ b/src/tbb/def/lin64-tbb.def @@ -107,6 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; +_ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE; +_ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index b9e0dbfef6..567ed5e468 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2071,14 +2071,12 @@ TEST_CASE("worker threads occupy slots in correct range") { #if TBB_PREVIEW_PARALLEL_BLOCK -//! \brief \ref interface \ref requirement -TEST_CASE("Check that leave faster with workers_leave::fast") { +template +std::size_t measure_avg_start_time(tbb::task_arena& ta, const F& ) { std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t num_runs = 1000; - std::vector start_times_for_delayed_leave; - start_times_for_delayed_leave.reserve(num_runs); - std::vector start_times_for_fast_leave; - start_times_for_fast_leave.reserve(num_runs); + std::size_t num_runs = 100; + std::vector longest_start_times; + longest_start_times.reserve(num_runs); std::vector start_times(num_threads); utils::SpinBarrier barrier(num_threads); @@ -2095,37 +2093,66 @@ TEST_CASE("Check that leave faster with workers_leave::fast") { return longest_time; }; - std::size_t avg_start_time_delayed = 0; - { - tbb::task_arena a(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); - - for (std::size_t i = 0; i < num_runs; ++i) { - a.execute([&] { - auto start_time = std::chrono::steady_clock::now(); - tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); - start_times_for_delayed_leave.push_back(get_longest_start(start_time)); - }); - std::this_thread::sleep_for(std::chrono::microseconds(i)); - } - avg_start_time_delayed = std::accumulate(start_times_for_delayed_leave.begin(), start_times_for_delayed_leave.end(), 0) / num_runs; + for (std::size_t i = 0; i < num_runs; ++i) { + ta.execute([&] { + auto start_time = std::chrono::steady_clock::now(); + tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + longest_start_times.push_back(get_longest_start(start_time)); + }); + std::this_thread::sleep_for(std::chrono::microseconds(500)); } + return std::accumulate(longest_start_times.begin(), longest_start_times.end(), 0) / num_runs; +} - std::size_t avg_start_time_fast = 0; - { - tbb::task_arena a(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); +//! \brief \ref interface \ref requirement +TEST_CASE("Check that workers leave faster with workers_leave::fast") { + std::size_t num_threads = utils::get_platform_max_threads(); + std::size_t successes = 0; + std::size_t num_trials = 100; + double required_success_rate = 0.8; + for (std::size_t i = 0; i < num_trials; ++i) { + std::size_t avg_start_time_delayed = 0; + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + avg_start_time_delayed = measure_avg_start_time(ta, []{}); + } - for (std::size_t i = 0; i < num_runs; ++i) { - a.execute([&] { - auto start_time = std::chrono::steady_clock::now(); - tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); - start_times_for_fast_leave.push_back(get_longest_start(start_time)); - }); - std::this_thread::sleep_for(std::chrono::microseconds(i)); + std::size_t avg_start_time_fast = 0; + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + avg_start_time_fast = measure_avg_start_time(ta, []{}); } - avg_start_time_fast = std::accumulate(start_times_for_fast_leave.begin(), start_times_for_fast_leave.end(), 0) / num_runs; + successes += avg_start_time_delayed < avg_start_time_fast ? 1 : 0; } + REQUIRE(successes >= num_trials * required_success_rate); +} - REQUIRE(avg_start_time_delayed < avg_start_time_fast); +TEST_CASE("Check that parallel_block sticks threads to task_arena") { + std::size_t num_threads = utils::get_platform_max_threads(); + std::size_t successes = 0; + std::size_t num_trials = 100; + double required_success_rate = 0.8; + for (std::size_t i = 0; i < num_trials; ++i) { + std::size_t avg_start_time_delayed = 0; + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + avg_start_time_delayed = measure_avg_start_time(ta, []{}); + } + std::this_thread::sleep_for(std::chrono::microseconds(1000)); + std::size_t avg_start_time_fast = 0; + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + // ta.parallel_block_start(); + avg_start_time_fast = measure_avg_start_time(ta, []{}); + // ta.parallel_block_end(true); + } + std::cout << avg_start_time_delayed << " " << avg_start_time_fast << std::endl; + successes += (avg_start_time_delayed < avg_start_time_fast ? + (double)avg_start_time_delayed / avg_start_time_fast >= 0.7 : + (double)avg_start_time_fast / avg_start_time_delayed >= 0.7) ? 1 : 0; + } + REQUIRE(successes >= num_trials * required_success_rate); } + #endif From 7f08af059c32281f2f6c7bad9114c8b84007c829 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Mon, 25 Nov 2024 16:12:22 +0100 Subject: [PATCH 05/35] Improve tests for parallel_block Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 19 ++++- src/tbb/waiters.h | 2 +- test/tbb/test_task_arena.cpp | 125 +++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 44 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 11c3e6dca3..f7d3b2ae92 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -479,15 +479,30 @@ class task_arena : public task_arena_base { } #if __TBB_PREVIEW_PARALLEL_BLOCK - void parallel_block_start() { + void start_parallel_block() { initialize(); r1::register_parallel_block(*this); + // Trigger worker threads to join arena enqueue([]{}); } - void parallel_block_end(bool set_one_time_fast_leave = false) { + void end_parallel_block(bool set_one_time_fast_leave = false) { __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); r1::unregister_parallel_block(*this, set_one_time_fast_leave); } + + class scoped_parallel_block { + task_arena& arena; + bool one_time_fast_leave; + public: + scoped_parallel_block(task_arena& ta, bool set_one_time_fast_leave = true) + : arena(ta), one_time_fast_leave(set_one_time_fast_leave) + { + arena.start_parallel_block(); + } + ~scoped_parallel_block() { + arena.end_parallel_block(one_time_fast_leave); + } + }; #endif #if __TBB_EXTRA_DEBUG diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index c27a6a4749..16ab9c5b07 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -54,7 +54,7 @@ class outermost_worker_waiter : public waiter_base { public: using waiter_base::waiter_base; - bool continue_execution(arena_slot& slot, d1::task*& t) const { + bool continue_execution(arena_slot& slot, d1::task*& t) { __TBB_ASSERT(t == nullptr, nullptr); if (is_worker_should_leave(slot)) { diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 567ed5e468..1185c0212a 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2071,10 +2071,15 @@ TEST_CASE("worker threads occupy slots in correct range") { #if TBB_PREVIEW_PARALLEL_BLOCK -template -std::size_t measure_avg_start_time(tbb::task_arena& ta, const F& ) { +struct dummy_func { + void operator()() const { + } +}; + +template +std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start_block = F1{}, const F2& end_block = F2{}) { std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t num_runs = 100; + std::size_t num_runs = 1000; std::vector longest_start_times; longest_start_times.reserve(num_runs); @@ -2096,63 +2101,101 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F& ) { for (std::size_t i = 0; i < num_runs; ++i) { ta.execute([&] { auto start_time = std::chrono::steady_clock::now(); + start_block(); tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + end_block(); longest_start_times.push_back(get_longest_start(start_time)); }); - std::this_thread::sleep_for(std::chrono::microseconds(500)); + std::this_thread::sleep_for(std::chrono::microseconds(i)); } - return std::accumulate(longest_start_times.begin(), longest_start_times.end(), 0) / num_runs; + // return std::accumulate(longest_start_times.begin(), longest_start_times.end(), 0) / num_runs; + return utils::median(longest_start_times.begin(), longest_start_times.end()); } //! \brief \ref interface \ref requirement TEST_CASE("Check that workers leave faster with workers_leave::fast") { std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t successes = 0; - std::size_t num_trials = 100; - double required_success_rate = 0.8; - for (std::size_t i = 0; i < num_trials; ++i) { - std::size_t avg_start_time_delayed = 0; - { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); - avg_start_time_delayed = measure_avg_start_time(ta, []{}); + std::size_t num_trials = 20; + std::vector avg_start_time_delayed(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + for (std::size_t i = 0; i < num_trials; ++i) { + auto avg_start_time = measure_avg_start_time(ta); + avg_start_time_delayed[i] = avg_start_time; } - - std::size_t avg_start_time_fast = 0; - { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); - avg_start_time_fast = measure_avg_start_time(ta, []{}); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::vector avg_start_time_fast(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + for (std::size_t i = 0; i < num_trials; ++i) { + auto avg_start_time = measure_avg_start_time(ta); + avg_start_time_fast[i] = avg_start_time; } - successes += avg_start_time_delayed < avg_start_time_fast ? 1 : 0; } - REQUIRE(successes >= num_trials * required_success_rate); + std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); + std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + REQUIRE(delayed_avg < fast_avg); } -TEST_CASE("Check that parallel_block sticks threads to task_arena") { +TEST_CASE("parallel_block retains workers in task_arena") { std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t successes = 0; - std::size_t num_trials = 100; - double required_success_rate = 0.8; - for (std::size_t i = 0; i < num_trials; ++i) { - std::size_t avg_start_time_delayed = 0; - { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); - avg_start_time_delayed = measure_avg_start_time(ta, []{}); + std::size_t num_trials = 20; + std::vector avg_start_time_delayed(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + for (std::size_t i = 0; i < num_trials; ++i) { + ta.start_parallel_block(); + auto avg_start_time = measure_avg_start_time(ta); + ta.end_parallel_block(true); + avg_start_time_delayed[i] = avg_start_time; } - std::this_thread::sleep_for(std::chrono::microseconds(1000)); - std::size_t avg_start_time_fast = 0; - { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); - // ta.parallel_block_start(); - avg_start_time_fast = measure_avg_start_time(ta, []{}); - // ta.parallel_block_end(true); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::vector avg_start_time_fast(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + for (std::size_t i = 0; i < num_trials; ++i) { + auto avg_start_time = measure_avg_start_time(ta); + avg_start_time_fast[i] = avg_start_time; } - std::cout << avg_start_time_delayed << " " << avg_start_time_fast << std::endl; - successes += (avg_start_time_delayed < avg_start_time_fast ? - (double)avg_start_time_delayed / avg_start_time_fast >= 0.7 : - (double)avg_start_time_fast / avg_start_time_delayed >= 0.7) ? 1 : 0; } - REQUIRE(successes >= num_trials * required_success_rate); + std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); + std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + REQUIRE(delayed_avg < fast_avg); } +TEST_CASE("Test one-time fast leave") { + std::size_t num_threads = utils::get_platform_max_threads(); + std::size_t num_trials = 20; + std::vector avg_start_time_delayed(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + for (std::size_t i = 0; i < num_trials; ++i) { + auto avg_start_time = measure_avg_start_time(ta); + avg_start_time_delayed[i] = avg_start_time; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::vector avg_start_time_fast(num_trials); + { + tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + auto start_block = [&ta] { ta.start_parallel_block(); }; + auto end_block = [&ta] { ta.end_parallel_block(true); }; + for (std::size_t i = 0; i < num_trials; ++i) { + auto avg_start_time = measure_avg_start_time(ta, start_block, end_block); + avg_start_time_fast[i] = avg_start_time; + } + } + std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); + std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + REQUIRE(delayed_avg < fast_avg); +} #endif From f6a139a6f9cb96895a6bc601aad6e796ecdc67f3 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 26 Nov 2024 12:59:23 +0100 Subject: [PATCH 06/35] Add workers_leave::automatic, let hybric systems take advantage of parallel_block Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 11 ++++++----- src/tbb/arena.h | 28 ++++++++++++++++------------ src/tbb/def/lin32-tbb.def | 2 ++ src/tbb/waiters.h | 11 +++++++---- test/tbb/test_task_arena.cpp | 6 +++--- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index f7d3b2ae92..69241b73c2 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -130,6 +130,7 @@ class task_arena_base { #if __TBB_PREVIEW_PARALLEL_BLOCK enum class workers_leave : int { + automatic = 0, fast = 1, delayed = 2 }; @@ -293,7 +294,7 @@ class task_arena : public task_arena_base { task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::delayed + , workers_leave wl = workers_leave::automatic #endif ) : task_arena_base(max_concurrency_, reserved_for_masters, a_priority @@ -308,7 +309,7 @@ class task_arena : public task_arena_base { task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::delayed + , workers_leave wl = workers_leave::automatic #endif ) : task_arena_base(constraints_, reserved_for_masters, a_priority @@ -350,7 +351,7 @@ class task_arena : public task_arena_base { explicit task_arena( attach ) : task_arena_base(automatic, 1, priority::normal #if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave::delayed + , workers_leave::automatic #endif ) // use default settings if attach fails { @@ -373,7 +374,7 @@ class task_arena : public task_arena_base { void initialize(int max_concurrency_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::delayed + , workers_leave wl = workers_leave::automatic #endif ) { @@ -394,7 +395,7 @@ class task_arena : public task_arena_base { void initialize(constraints constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::delayed + , workers_leave wl = workers_leave::automatic #endif ) { diff --git a/src/tbb/arena.h b/src/tbb/arena.h index cb921fab6a..cfb26067d5 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -181,15 +181,19 @@ class atomic_flag { #if __TBB_PREVIEW_PARALLEL_BLOCK class thread_leave_manager { - static const std::uintptr_t FAST_LEAVE = 1; - static const std::uintptr_t ONE_TIME_FAST_LEAVE = 1 << 1; - static const std::uintptr_t DELAYED_LEAVE = 1 << 2; - static const std::uintptr_t PARALLEL_BLOCK = 1 << 3; - static const std::uintptr_t PARALLEL_BLOCK_MASK = ~((1LLU << 32) - 1) & ~(0x7); + static const std::uint64_t FAST_LEAVE = 1; + static const std::uint64_t ONE_TIME_FAST_LEAVE = 1 << 1; + static const std::uint64_t DELAYED_LEAVE = 1 << 2; + static const std::uint64_t PARALLEL_BLOCK = 1 << 3; + static const std::uint64_t PARALLEL_BLOCK_MASK = ~((1LLU << 32) - 1) & ~(0x7); - std::atomic my_state{0}; + std::atomic my_state{0}; public: void set_initial_state(tbb::task_arena::workers_leave wl) { + if (wl == tbb::task_arena::workers_leave::automatic) { + wl = governor::hybrid_cpu() ? tbb::task_arena::workers_leave::fast + : tbb::task_arena::workers_leave::delayed; + } if (wl == tbb::task_arena::workers_leave::delayed) { my_state.store(DELAYED_LEAVE, std::memory_order_relaxed); } else { @@ -198,7 +202,7 @@ class thread_leave_manager { } void restore_state_if_needed() { - std::uintptr_t curr = ONE_TIME_FAST_LEAVE; + std::uint64_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { // Potentially can override desicion of the parallel block from future epoch // but it is not a problem because it does not violate the correctness @@ -209,8 +213,8 @@ class thread_leave_manager { void register_parallel_block() { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uintptr_t prev = my_state.load(std::memory_order_relaxed); - std::uintptr_t desired{}; + std::uint64_t prev = my_state.load(std::memory_order_relaxed); + std::uint64_t desired{}; do { if (prev & PARALLEL_BLOCK_MASK) { desired = PARALLEL_BLOCK + prev; @@ -225,8 +229,8 @@ class thread_leave_manager { void unregister_parallel_block(bool enable_fast_leave) { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uintptr_t prev = my_state.load(std::memory_order_relaxed); - std::uintptr_t desired{}; + std::uint64_t prev = my_state.load(std::memory_order_relaxed); + std::uint64_t desired{}; do { if (((prev - PARALLEL_BLOCK) & PARALLEL_BLOCK_MASK) != 0) { desired = prev - PARALLEL_BLOCK; @@ -239,7 +243,7 @@ class thread_leave_manager { bool should_leave() { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uintptr_t curr = my_state.load(std::memory_order_relaxed); + std::uint64_t curr = my_state.load(std::memory_order_relaxed); return curr == FAST_LEAVE || curr == ONE_TIME_FAST_LEAVE; } }; diff --git a/src/tbb/def/lin32-tbb.def b/src/tbb/def/lin32-tbb.def index 737e8ec2af..a33fabbf16 100644 --- a/src/tbb/def/lin32-tbb.def +++ b/src/tbb/def/lin32-tbb.def @@ -107,6 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; +_ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE; +_ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index 16ab9c5b07..e7a38db2de 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -58,9 +58,11 @@ class outermost_worker_waiter : public waiter_base { __TBB_ASSERT(t == nullptr, nullptr); if (is_worker_should_leave(slot)) { - if (!governor::hybrid_cpu() + if ( #if __TBB_PREVIEW_PARALLEL_BLOCK - && !my_arena.my_thread_leave.should_leave() + !my_arena.my_thread_leave.should_leave() +#else + !governor::hybrid_cpu() #endif ) { @@ -75,10 +77,11 @@ class outermost_worker_waiter : public waiter_base { return true; } - if (my_arena.my_threading_control->is_any_other_client_active() + if ( #if __TBB_PREVIEW_PARALLEL_BLOCK - || my_arena.my_thread_leave.should_leave() + my_arena.my_thread_leave.should_leave() || #endif + my_arena.my_threading_control->is_any_other_client_active() ) { break; diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 1185c0212a..9007b5c2df 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2137,7 +2137,7 @@ TEST_CASE("Check that workers leave faster with workers_leave::fast") { std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; - REQUIRE(delayed_avg < fast_avg); + WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster with delayed leave"); } TEST_CASE("parallel_block retains workers in task_arena") { @@ -2166,7 +2166,7 @@ TEST_CASE("parallel_block retains workers in task_arena") { std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; - REQUIRE(delayed_avg < fast_avg); + WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster when using parallel_block"); } TEST_CASE("Test one-time fast leave") { @@ -2195,7 +2195,7 @@ TEST_CASE("Test one-time fast leave") { std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; - REQUIRE(delayed_avg < fast_avg); + WARN_MESSAGE(delayed_avg < fast_avg, "Expected one-time fast leave setting to slow workers to start new work"); } #endif From 0a9faed4736262d06609cd909dc8ac6f87690cb6 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 26 Nov 2024 17:30:07 +0100 Subject: [PATCH 07/35] Fix Windows compilation Signed-off-by: Isaev, Ilya --- src/tbb/def/win64-tbb.def | 2 ++ test/tbb/test_task_arena.cpp | 31 +++++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/tbb/def/win64-tbb.def b/src/tbb/def/win64-tbb.def index 96bafc0163..b68a46da2a 100644 --- a/src/tbb/def/win64-tbb.def +++ b/src/tbb/def/win64-tbb.def @@ -101,6 +101,8 @@ EXPORTS ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@PEAVtask_arena_base@523@@Z ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@PEAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGAEBVtask_arena_base@d1@23@@Z +?register_parallel_block@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@@Z +?unregister_parallel_block@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@_N@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 9007b5c2df..ef7a9b791d 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -14,6 +14,7 @@ limitations under the License. */ +#include "common/dummy_body.h" #include "common/test.h" #define __TBB_EXTRA_DEBUG 1 @@ -41,6 +42,7 @@ #include #include #include +#include //#include "harness_fp.h" @@ -2099,6 +2101,7 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start_block = }; for (std::size_t i = 0; i < num_runs; ++i) { + // std::cout << i << std::endl; ta.execute([&] { auto start_time = std::chrono::steady_clock::now(); start_block(); @@ -2106,9 +2109,9 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start_block = end_block(); longest_start_times.push_back(get_longest_start(start_time)); }); - std::this_thread::sleep_for(std::chrono::microseconds(i)); + // std::this_thread::sleep_for(std::chrono::microseconds(500)); + utils::doDummyWork(i*100); } - // return std::accumulate(longest_start_times.begin(), longest_start_times.end(), 0) / num_runs; return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -2118,7 +2121,7 @@ TEST_CASE("Check that workers leave faster with workers_leave::fast") { std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_delayed[i] = avg_start_time; @@ -2127,7 +2130,7 @@ TEST_CASE("Check that workers leave faster with workers_leave::fast") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_fast[i] = avg_start_time; @@ -2135,8 +2138,8 @@ TEST_CASE("Check that workers leave faster with workers_leave::fast") { } std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster with delayed leave"); } @@ -2145,7 +2148,7 @@ TEST_CASE("parallel_block retains workers in task_arena") { std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); for (std::size_t i = 0; i < num_trials; ++i) { ta.start_parallel_block(); auto avg_start_time = measure_avg_start_time(ta); @@ -2156,7 +2159,7 @@ TEST_CASE("parallel_block retains workers in task_arena") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_fast[i] = avg_start_time; @@ -2164,8 +2167,8 @@ TEST_CASE("parallel_block retains workers in task_arena") { } std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster when using parallel_block"); } @@ -2174,7 +2177,7 @@ TEST_CASE("Test one-time fast leave") { std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_delayed[i] = avg_start_time; @@ -2183,7 +2186,7 @@ TEST_CASE("Test one-time fast leave") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta(num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); auto start_block = [&ta] { ta.start_parallel_block(); }; auto end_block = [&ta] { ta.end_parallel_block(true); }; for (std::size_t i = 0; i < num_trials; ++i) { @@ -2193,8 +2196,8 @@ TEST_CASE("Test one-time fast leave") { } std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + num_trials * 0.9, 0) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + num_trials * 0.9, 0) / num_trials; + auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; + auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; WARN_MESSAGE(delayed_avg < fast_avg, "Expected one-time fast leave setting to slow workers to start new work"); } From c1802a11be3073183a1c18a7a4accb93c7a7dbf5 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 27 Nov 2024 17:13:56 +0100 Subject: [PATCH 08/35] Add win32 exports Signed-off-by: Isaev, Ilya --- src/tbb/def/win32-tbb.def | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tbb/def/win32-tbb.def b/src/tbb/def/win32-tbb.def index 94b5441701..0b2695c80a 100644 --- a/src/tbb/def/win32-tbb.def +++ b/src/tbb/def/win32-tbb.def @@ -101,6 +101,8 @@ EXPORTS ?wait@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@@Z ?enqueue@r1@detail@tbb@@YAXAAVtask@d1@23@AAVtask_group_context@523@PAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGABVtask_arena_base@d1@23@@Z +?register_parallel_block@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@@Z +?unregister_parallel_block@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@_N@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ From ecd5a9e6a0cfa691482b41461276a8a04d716dec Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 27 Nov 2024 17:39:49 +0100 Subject: [PATCH 09/35] Add mac64 symbols Signed-off-by: Isaev, Ilya --- src/tbb/def/mac64-tbb.def | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tbb/def/mac64-tbb.def b/src/tbb/def/mac64-tbb.def index 38bc48d30e..403259eec1 100644 --- a/src/tbb/def/mac64-tbb.def +++ b/src/tbb/def/mac64-tbb.def @@ -109,6 +109,8 @@ __ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE __ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE __ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE __ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE +__ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE +__ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb # System topology parsing and threads pinning (governor.cpp) __ZN3tbb6detail2r115numa_node_countEv From 0d32f80a983790a1a9f68d56b0993ce3a9d088be Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Thu, 28 Nov 2024 16:31:15 +0100 Subject: [PATCH 10/35] Correct mask, add a clarifying comment to it Signed-off-by: Isaev, Ilya --- src/tbb/arena.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index cfb26067d5..04f26fb7ed 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -185,7 +185,9 @@ class thread_leave_manager { static const std::uint64_t ONE_TIME_FAST_LEAVE = 1 << 1; static const std::uint64_t DELAYED_LEAVE = 1 << 2; static const std::uint64_t PARALLEL_BLOCK = 1 << 3; - static const std::uint64_t PARALLEL_BLOCK_MASK = ~((1LLU << 32) - 1) & ~(0x7); + // Use 29 bits for the parallel block state + reference counter, + // reserve 32 most significant bits. + static const std::uint64_t PARALLEL_BLOCK_MASK = ((1LLU << 32) - 1) & (PARALLEL_BLOCK - 1); std::atomic my_state{0}; public: From f61fc1df5113bd942218cdff1e9f2c90e02984a0 Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Thu, 28 Nov 2024 16:33:48 +0100 Subject: [PATCH 11/35] Apply suggested typo fix Co-authored-by: Aleksei Fedotov --- src/tbb/arena.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 04f26fb7ed..698cac68dd 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -206,7 +206,7 @@ class thread_leave_manager { void restore_state_if_needed() { std::uint64_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { - // Potentially can override desicion of the parallel block from future epoch + // Potentially can override decision of the parallel block from future epoch // but it is not a problem because it does not violate the correctness my_state.compare_exchange_strong(curr, DELAYED_LEAVE); } From 17f1dac325c45fb5fa7a8f3b7ac0074e0df52aa5 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Fri, 29 Nov 2024 13:14:19 +0100 Subject: [PATCH 12/35] Rename block to phase Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/detail/_config.h | 4 +- include/oneapi/tbb/task_arena.h | 94 ++++++++++++++--------------- src/tbb/arena.cpp | 20 +++--- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/include/oneapi/tbb/detail/_config.h b/include/oneapi/tbb/detail/_config.h index bf2c085609..bd7c50e7b4 100644 --- a/include/oneapi/tbb/detail/_config.h +++ b/include/oneapi/tbb/detail/_config.h @@ -534,8 +534,8 @@ #define __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #endif -#if TBB_PREVIEW_PARALLEL_BLOCK || __TBB_BUILD -#define __TBB_PREVIEW_PARALLEL_BLOCK 1 +#if TBB_PREVIEW_PARALLEL_PHASE || __TBB_BUILD +#define __TBB_PREVIEW_PARALLEL_PHASE 1 #endif #endif // __TBB_detail__config_H diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 69241b73c2..80c2ab8082 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -96,9 +96,9 @@ TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_arena_base*); TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_group_context&, d1::task_arena_base*); TBB_EXPORT void __TBB_EXPORTED_FUNC submit(d1::task&, d1::task_group_context&, arena*, std::uintptr_t); -#if __TBB_PREVIEW_PARALLEL_BLOCK -TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_block(d1::task_arena_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_block(d1::task_arena_base&, bool); +#if __TBB_PREVIEW_PARALLEL_PHASE +TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base&); +TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base&, std::uintptr_t); #endif } // namespace r1 @@ -128,8 +128,8 @@ class task_arena_base { high = 3 * priority_stride }; -#if __TBB_PREVIEW_PARALLEL_BLOCK - enum class workers_leave : int { +#if __TBB_PREVIEW_PARALLEL_PHASE + enum class leave_policy : int { automatic = 0, fast = 1, delayed = 2 @@ -168,9 +168,9 @@ class task_arena_base { //! Number of threads per core int my_max_threads_per_core; -#if __TBB_PREVIEW_PARALLEL_BLOCK - //! Defines the initial behavior of the workers_leave state machine - workers_leave my_workers_leave; +#if __TBB_PREVIEW_PARALLEL_PHASE + //! Defines the initial behavior of the leave_policy state machine + leave_policy my_leave_policy; #endif // Backward compatibility checks. @@ -187,8 +187,8 @@ class task_arena_base { }; task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl #endif ) : my_version_and_traits(default_flags | core_type_support_flag) @@ -200,15 +200,15 @@ class task_arena_base { , my_numa_id(automatic) , my_core_type(automatic) , my_max_threads_per_core(automatic) -#if __TBB_PREVIEW_PARALLEL_BLOCK - , my_workers_leave(wl) +#if __TBB_PREVIEW_PARALLEL_PHASE + , my_leave_policy(wl) #endif {} #if __TBB_ARENA_BINDING task_arena_base(const constraints& constraints_, unsigned reserved_for_masters, priority a_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl #endif ) : my_version_and_traits(default_flags | core_type_support_flag) @@ -220,8 +220,8 @@ class task_arena_base { , my_numa_id(constraints_.numa_id) , my_core_type(constraints_.core_type) , my_max_threads_per_core(constraints_.max_threads_per_core) -#if __TBB_PREVIEW_PARALLEL_BLOCK - , my_workers_leave(wl) +#if __TBB_PREVIEW_PARALLEL_PHASE + , my_leave_policy(wl) #endif {} #endif /*__TBB_ARENA_BINDING*/ @@ -293,12 +293,12 @@ class task_arena : public task_arena_base { **/ task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, priority a_priority = priority::normal -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::automatic +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl = leave_policy::automatic #endif ) : task_arena_base(max_concurrency_, reserved_for_masters, a_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE , wl #endif ) @@ -308,12 +308,12 @@ class task_arena : public task_arena_base { //! Creates task arena pinned to certain NUMA node task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::automatic +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl = leave_policy::automatic #endif ) : task_arena_base(constraints_, reserved_for_masters, a_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE , wl #endif ) @@ -328,8 +328,8 @@ class task_arena : public task_arena_base { .set_core_type(a.my_core_type) .set_max_threads_per_core(a.my_max_threads_per_core) , a.my_num_reserved_slots, a.my_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK - , a.my_workers_leave +#if __TBB_PREVIEW_PARALLEL_PHASE + , a.my_leave_policy #endif ) {} @@ -337,8 +337,8 @@ class task_arena : public task_arena_base { //! Copies settings from another task_arena task_arena(const task_arena& a) // copy settings but not the reference or instance : task_arena_base(a.my_max_concurrency, a.my_num_reserved_slots, a.my_priority -#if __TBB_PREVIEW_PARALLEL_BLOCK - , a.my_workers_leave +#if __TBB_PREVIEW_PARALLEL_PHASE + , a.my_leave_policy #endif ) {} @@ -350,8 +350,8 @@ class task_arena : public task_arena_base { //! Creates an instance of task_arena attached to the current arena of the thread explicit task_arena( attach ) : task_arena_base(automatic, 1, priority::normal -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave::automatic +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy::automatic #endif ) // use default settings if attach fails { @@ -373,8 +373,8 @@ class task_arena : public task_arena_base { //! Overrides concurrency level and forces initialization of internal representation void initialize(int max_concurrency_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::automatic +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl = leave_policy::automatic #endif ) { @@ -383,8 +383,8 @@ class task_arena : public task_arena_base { my_max_concurrency = max_concurrency_; my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; -#if __TBB_PREVIEW_PARALLEL_BLOCK - my_workers_leave = wl; +#if __TBB_PREVIEW_PARALLEL_PHASE + my_leave_policy = wl; #endif r1::initialize(*this); mark_initialized(); @@ -394,8 +394,8 @@ class task_arena : public task_arena_base { #if __TBB_ARENA_BINDING void initialize(constraints constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal -#if __TBB_PREVIEW_PARALLEL_BLOCK - , workers_leave wl = workers_leave::automatic +#if __TBB_PREVIEW_PARALLEL_PHASE + , leave_policy wl = leave_policy::automatic #endif ) { @@ -407,8 +407,8 @@ class task_arena : public task_arena_base { my_max_threads_per_core = constraints_.max_threads_per_core; my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; -#if __TBB_PREVIEW_PARALLEL_BLOCK - my_workers_leave = wl; +#if __TBB_PREVIEW_PARALLEL_PHASE + my_leave_policy = wl; #endif r1::initialize(*this); mark_initialized(); @@ -479,29 +479,29 @@ class task_arena : public task_arena_base { return execute_impl(f); } -#if __TBB_PREVIEW_PARALLEL_BLOCK - void start_parallel_block() { +#if __TBB_PREVIEW_PARALLEL_PHASE + void start_parallel_phase() { initialize(); - r1::register_parallel_block(*this); + r1::register_parallel_phase(*this); // Trigger worker threads to join arena enqueue([]{}); } - void end_parallel_block(bool set_one_time_fast_leave = false) { + void end_parallel_phase(bool with_fast_leave = false) { __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); - r1::unregister_parallel_block(*this, set_one_time_fast_leave); + r1::unregister_parallel_phase(*this, with_fast_leave); } - class scoped_parallel_block { + class scoped_parallel_phase { task_arena& arena; bool one_time_fast_leave; public: - scoped_parallel_block(task_arena& ta, bool set_one_time_fast_leave = true) - : arena(ta), one_time_fast_leave(set_one_time_fast_leave) + scoped_parallel_phase(task_arena& ta, bool with_fast_leave = true) + : arena(ta), one_time_fast_leave(with_fast_leave) { - arena.start_parallel_block(); + arena.start_parallel_phase(); } - ~scoped_parallel_block() { - arena.end_parallel_block(one_time_fast_leave); + ~scoped_parallel_phase() { + arena.end_parallel_phase(one_time_fast_leave); } }; #endif diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 2b267a3682..6f74049015 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -529,8 +529,8 @@ struct task_arena_impl { static int max_concurrency(const d1::task_arena_base*); static void enqueue(d1::task&, d1::task_group_context*, d1::task_arena_base*); static d1::slot_id execution_slot(const d1::task_arena_base&); - static void register_parallel_block(d1::task_arena_base&); - static void unregister_parallel_block(d1::task_arena_base&, bool); + static void register_parallel_phase(d1::task_arena_base&); + static void unregister_parallel_phase(d1::task_arena_base&, bool); }; void __TBB_EXPORTED_FUNC initialize(d1::task_arena_base& ta) { @@ -565,12 +565,12 @@ d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::task_arena_base& arena) return task_arena_impl::execution_slot(arena); } -void __TBB_EXPORTED_FUNC register_parallel_block(d1::task_arena_base& ta) { - task_arena_impl::register_parallel_block(ta); +void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base& ta) { + task_arena_impl::register_parallel_phase(ta); } -void __TBB_EXPORTED_FUNC unregister_parallel_block(d1::task_arena_base& ta, bool one_time_fast_leave) { - task_arena_impl::unregister_parallel_block(ta, one_time_fast_leave); +void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base& ta, std::uintptr_t flags) { + task_arena_impl::unregister_parallel_phase(ta, flags); } void task_arena_impl::initialize(d1::task_arena_base& ta) { @@ -919,12 +919,12 @@ int task_arena_impl::max_concurrency(const d1::task_arena_base *ta) { } #if __TBB_PREVIEW_PARALLEL_BLOCK -void task_arena_impl::register_parallel_block(d1::task_arena_base& ta) { - ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.register_parallel_block(); +void task_arena_impl::register_parallel_phase(d1::task_arena_base& ta) { + ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.register_parallel_phase(); } -void task_arena_impl::unregister_parallel_block(d1::task_arena_base& ta, bool one_time_fast_leave) { - ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.unregister_parallel_block(one_time_fast_leave); +void task_arena_impl::unregister_parallel_phase(d1::task_arena_base& ta, bool with_fast_leave) { + ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.unregister_parallel_phase(with_fast_leave); } #endif From aa7cc2abee47c4f45b662541065b59e674139f96 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Mon, 2 Dec 2024 10:57:57 +0100 Subject: [PATCH 13/35] Align with RFC, utilize my_version_and_traits, improve readability Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 98 +++++++++++++++++++++------------ src/tbb/arena.cpp | 58 ++++++++++--------- src/tbb/arena.h | 55 +++++++++--------- src/tbb/def/lin32-tbb.def | 4 +- src/tbb/def/lin64-tbb.def | 4 +- src/tbb/waiters.h | 28 ++++------ test/common/config.h | 4 +- test/tbb/test_task_arena.cpp | 36 ++++++------ 8 files changed, 156 insertions(+), 131 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 80c2ab8082..7452938899 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -27,6 +27,8 @@ #include "detail/_task.h" #include "detail/_task_handle.h" +#include "oneapi/tbb/detail/_assert.h" +#include #if __TBB_ARENA_BINDING #include "info.h" @@ -97,8 +99,8 @@ TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_group_context&, TBB_EXPORT void __TBB_EXPORTED_FUNC submit(d1::task&, d1::task_group_context&, arena*, std::uintptr_t); #if __TBB_PREVIEW_PARALLEL_PHASE -TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base&, std::uintptr_t); +TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base*, std::uintptr_t); +TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base*, std::uintptr_t); #endif } // namespace r1 @@ -118,6 +120,11 @@ namespace d1 { static constexpr unsigned num_priority_levels = 3; static constexpr int priority_stride = INT_MAX / (num_priority_levels + 1); +#if __TBB_PREVIEW_PARALLEL_PHASE +static constexpr int leave_policy_trait_offset = 1; +static constexpr int leave_policy_trait_mask = ~((1 << leave_policy_trait_offset) - 1); +#endif + class task_arena_base { friend struct r1::task_arena_impl; friend void r1::observe(d1::task_scheduler_observer&, bool); @@ -131,8 +138,7 @@ class task_arena_base { #if __TBB_PREVIEW_PARALLEL_PHASE enum class leave_policy : int { automatic = 0, - fast = 1, - delayed = 2 + fast = 1 }; #endif @@ -168,11 +174,6 @@ class task_arena_base { //! Number of threads per core int my_max_threads_per_core; -#if __TBB_PREVIEW_PARALLEL_PHASE - //! Defines the initial behavior of the leave_policy state machine - leave_policy my_leave_policy; -#endif - // Backward compatibility checks. core_type_id core_type() const { return (my_version_and_traits & core_type_support_flag) == core_type_support_flag ? my_core_type : automatic; @@ -181,17 +182,32 @@ class task_arena_base { return (my_version_and_traits & core_type_support_flag) == core_type_support_flag ? my_max_threads_per_core : automatic; } +#if __TBB_PREVIEW_PARALLEL_PHASE + int leave_policy_to_traits(leave_policy lp) const { + return static_cast(lp) << leave_policy_trait_offset; + } + + leave_policy get_leave_policy() const { + int underlying_policy_v = (my_version_and_traits & leave_policy_trait_mask) >> leave_policy_trait_offset; + return static_cast(underlying_policy_v); + } + + void set_leave_policy(leave_policy lp) { + my_version_and_traits = (my_version_and_traits & ~leave_policy_trait_mask) | leave_policy_to_traits(lp); + } +#endif + enum { - default_flags = 0 - , core_type_support_flag = 1 + default_flags = 0, + core_type_support_flag = 1, }; task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl + , leave_policy lp #endif ) - : my_version_and_traits(default_flags | core_type_support_flag) + : my_version_and_traits(default_flags | core_type_support_flag | leave_policy_to_traits(lp)) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) , my_max_concurrency(max_concurrency) @@ -200,18 +216,15 @@ class task_arena_base { , my_numa_id(automatic) , my_core_type(automatic) , my_max_threads_per_core(automatic) -#if __TBB_PREVIEW_PARALLEL_PHASE - , my_leave_policy(wl) -#endif {} #if __TBB_ARENA_BINDING task_arena_base(const constraints& constraints_, unsigned reserved_for_masters, priority a_priority #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl + , leave_policy lp #endif ) - : my_version_and_traits(default_flags | core_type_support_flag) + : my_version_and_traits(default_flags | core_type_support_flag | leave_policy_to_traits(lp)) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) , my_max_concurrency(constraints_.max_concurrency) @@ -220,9 +233,6 @@ class task_arena_base { , my_numa_id(constraints_.numa_id) , my_core_type(constraints_.core_type) , my_max_threads_per_core(constraints_.max_threads_per_core) -#if __TBB_PREVIEW_PARALLEL_PHASE - , my_leave_policy(wl) -#endif {} #endif /*__TBB_ARENA_BINDING*/ public: @@ -294,12 +304,12 @@ class task_arena : public task_arena_base { task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl = leave_policy::automatic + , leave_policy lp = leave_policy::automatic #endif ) : task_arena_base(max_concurrency_, reserved_for_masters, a_priority #if __TBB_PREVIEW_PARALLEL_PHASE - , wl + , lp #endif ) {} @@ -309,12 +319,12 @@ class task_arena : public task_arena_base { task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl = leave_policy::automatic + , leave_policy lp = leave_policy::automatic #endif ) : task_arena_base(constraints_, reserved_for_masters, a_priority #if __TBB_PREVIEW_PARALLEL_PHASE - , wl + , lp #endif ) {} @@ -329,16 +339,19 @@ class task_arena : public task_arena_base { .set_max_threads_per_core(a.my_max_threads_per_core) , a.my_num_reserved_slots, a.my_priority #if __TBB_PREVIEW_PARALLEL_PHASE - , a.my_leave_policy + , a.get_leave_policy() #endif - ) + ) + {} #else //! Copies settings from another task_arena task_arena(const task_arena& a) // copy settings but not the reference or instance - : task_arena_base(a.my_max_concurrency, a.my_num_reserved_slots, a.my_priority + : task_arena_base(a.my_max_concurrency, + a.my_num_reserved_slots, + a.my_priority, #if __TBB_PREVIEW_PARALLEL_PHASE - , a.my_leave_policy + a.get_leave_policy() #endif ) {} @@ -374,7 +387,7 @@ class task_arena : public task_arena_base { void initialize(int max_concurrency_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl = leave_policy::automatic + , leave_policy lp = leave_policy::automatic #endif ) { @@ -384,7 +397,7 @@ class task_arena : public task_arena_base { my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; #if __TBB_PREVIEW_PARALLEL_PHASE - my_leave_policy = wl; + set_leave_policy(lp); #endif r1::initialize(*this); mark_initialized(); @@ -395,7 +408,7 @@ class task_arena : public task_arena_base { void initialize(constraints constraints_, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy wl = leave_policy::automatic + , leave_policy lp = leave_policy::automatic #endif ) { @@ -408,7 +421,7 @@ class task_arena : public task_arena_base { my_num_reserved_slots = reserved_for_masters; my_priority = a_priority; #if __TBB_PREVIEW_PARALLEL_PHASE - my_leave_policy = wl; + set_leave_policy(lp); #endif r1::initialize(*this); mark_initialized(); @@ -482,13 +495,13 @@ class task_arena : public task_arena_base { #if __TBB_PREVIEW_PARALLEL_PHASE void start_parallel_phase() { initialize(); - r1::register_parallel_phase(*this); + r1::register_parallel_phase(this, 0); // Trigger worker threads to join arena enqueue([]{}); } void end_parallel_phase(bool with_fast_leave = false) { __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); - r1::unregister_parallel_phase(*this, with_fast_leave); + r1::unregister_parallel_phase(this, with_fast_leave); } class scoped_parallel_phase { @@ -574,6 +587,16 @@ inline void enqueue(F&& f) { enqueue_impl(std::forward(f), nullptr); } +#if __TBB_PREVIEW_PARALLEL_PHASE +inline void start_parallel_phase() { + r1::register_parallel_phase(nullptr, 0); +} + +inline void end_parallel_phase(bool with_fast_leave) { + r1::unregister_parallel_phase(nullptr, with_fast_leave); +} +#endif + using r1::submit; } // namespace d1 @@ -593,6 +616,11 @@ using detail::d1::max_concurrency; using detail::d1::isolate; using detail::d1::enqueue; + +#if __TBB_PREVIEW_PARALLEL_PHASE +using detail::d1::start_parallel_phase; +using detail::d1::end_parallel_phase; +#endif } // namespace this_task_arena } // inline namespace v1 diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 6f74049015..bdced70b4c 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -14,6 +14,7 @@ limitations under the License. */ +#include "oneapi/tbb/detail/_assert.h" #include "task_dispatcher.h" #include "governor.h" #include "threading_control.h" @@ -242,8 +243,8 @@ void arena::process(thread_data& tls) { } arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy lp #endif ) { @@ -282,15 +283,15 @@ arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserv #endif my_mandatory_requests = 0; -#if __TBB_PREVIEW_PARALLEL_BLOCK - my_thread_leave.set_initial_state(wl); +#if __TBB_PREVIEW_PARALLEL_PHASE + my_thread_leave.set_initial_state(lp); #endif } arena& arena::allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy wl #endif ) { @@ -304,7 +305,7 @@ arena& arena::allocate_arena(threading_control* control, unsigned num_slots, uns return *new( storage + num_arena_slots(num_slots, num_reserved_slots) * sizeof(mail_outbox) ) arena(control, num_slots, num_reserved_slots, priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE , wl #endif ); @@ -357,7 +358,7 @@ bool arena::has_enqueued_tasks() { } void arena::request_workers(int mandatory_delta, int workers_delta, bool wakeup_threads) { -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE my_thread_leave.restore_state_if_needed(); #endif my_threading_control->adjust_demand(my_tc_client, mandatory_delta, workers_delta); @@ -463,18 +464,19 @@ void arena::enqueue_task(d1::task& t, d1::task_group_context& ctx, thread_data& advertise_new_work(); } -arena& arena::create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl +arena &arena::create(threading_control *control, unsigned num_slots, + unsigned num_reserved_slots, unsigned arena_priority_level, + d1::constraints constraints +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy lp #endif -) -{ +) { __TBB_ASSERT(num_slots > 0, NULL); __TBB_ASSERT(num_reserved_slots <= num_slots, NULL); // Add public market reference for an external thread/task_arena (that adds an internal reference in exchange). arena& a = arena::allocate_arena(control, num_slots, num_reserved_slots, arena_priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK - , wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , lp #endif ); a.my_tc_client = control->create_client(a); @@ -529,8 +531,8 @@ struct task_arena_impl { static int max_concurrency(const d1::task_arena_base*); static void enqueue(d1::task&, d1::task_group_context*, d1::task_arena_base*); static d1::slot_id execution_slot(const d1::task_arena_base&); - static void register_parallel_phase(d1::task_arena_base&); - static void unregister_parallel_phase(d1::task_arena_base&, bool); + static void register_parallel_phase(d1::task_arena_base*); + static void unregister_parallel_phase(d1::task_arena_base*, bool); }; void __TBB_EXPORTED_FUNC initialize(d1::task_arena_base& ta) { @@ -565,11 +567,11 @@ d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::task_arena_base& arena) return task_arena_impl::execution_slot(arena); } -void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base& ta) { +void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base* ta, std::uintptr_t /*reserved*/) { task_arena_impl::register_parallel_phase(ta); } -void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base& ta, std::uintptr_t flags) { +void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { task_arena_impl::unregister_parallel_phase(ta, flags); } @@ -608,8 +610,8 @@ void task_arena_impl::initialize(d1::task_arena_base& ta) { unsigned priority_level = arena_priority_level(ta.my_priority); threading_control* thr_control = threading_control::register_public_reference(); arena& a = arena::create(thr_control, unsigned(ta.my_max_concurrency), ta.my_num_reserved_slots, priority_level, arena_constraints -#if __TBB_PREVIEW_PARALLEL_BLOCK - , ta.my_workers_leave +#if __TBB_PREVIEW_PARALLEL_PHASE + , ta.get_leave_policy() #endif ); @@ -918,13 +920,17 @@ int task_arena_impl::max_concurrency(const d1::task_arena_base *ta) { return int(governor::default_num_threads()); } -#if __TBB_PREVIEW_PARALLEL_BLOCK -void task_arena_impl::register_parallel_phase(d1::task_arena_base& ta) { - ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.register_parallel_phase(); +#if __TBB_PREVIEW_PARALLEL_PHASE +void task_arena_impl::register_parallel_phase(d1::task_arena_base* ta) { + arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; + __TBB_ASSERT(a, nullptr); + a->my_thread_leave.register_parallel_phase(); } -void task_arena_impl::unregister_parallel_phase(d1::task_arena_base& ta, bool with_fast_leave) { - ta.my_arena.load(std::memory_order_relaxed)->my_thread_leave.unregister_parallel_phase(with_fast_leave); +void task_arena_impl::unregister_parallel_phase(d1::task_arena_base* ta, bool with_fast_leave) { + arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; + __TBB_ASSERT(a, nullptr); + a->my_thread_leave.unregister_parallel_phase(with_fast_leave); } #endif diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 698cac68dd..351f2fbc8b 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -179,25 +179,22 @@ class atomic_flag { } }; -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE class thread_leave_manager { static const std::uint64_t FAST_LEAVE = 1; static const std::uint64_t ONE_TIME_FAST_LEAVE = 1 << 1; static const std::uint64_t DELAYED_LEAVE = 1 << 2; - static const std::uint64_t PARALLEL_BLOCK = 1 << 3; + static const std::uint64_t PARALLEL_PHASE = 1 << 3; // Use 29 bits for the parallel block state + reference counter, // reserve 32 most significant bits. - static const std::uint64_t PARALLEL_BLOCK_MASK = ((1LLU << 32) - 1) & (PARALLEL_BLOCK - 1); + static const std::uint64_t PARALLEL_PHASE_MASK = ((1LLU << 32) - 1) & (PARALLEL_PHASE - 1); std::atomic my_state{0}; public: - void set_initial_state(tbb::task_arena::workers_leave wl) { - if (wl == tbb::task_arena::workers_leave::automatic) { - wl = governor::hybrid_cpu() ? tbb::task_arena::workers_leave::fast - : tbb::task_arena::workers_leave::delayed; - } - if (wl == tbb::task_arena::workers_leave::delayed) { - my_state.store(DELAYED_LEAVE, std::memory_order_relaxed); + void set_initial_state(tbb::task_arena::leave_policy wl) { + if (wl == tbb::task_arena::leave_policy::automatic) { + std::uint64_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; + my_state.store(platform_policy, std::memory_order_relaxed); } else { my_state.store(FAST_LEAVE, std::memory_order_relaxed); } @@ -212,44 +209,44 @@ class thread_leave_manager { } } - void register_parallel_block() { + void register_parallel_phase() { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); std::uint64_t prev = my_state.load(std::memory_order_relaxed); std::uint64_t desired{}; do { - if (prev & PARALLEL_BLOCK_MASK) { - desired = PARALLEL_BLOCK + prev; + if (prev & PARALLEL_PHASE_MASK) { + desired = PARALLEL_PHASE + prev; } else if (prev == ONE_TIME_FAST_LEAVE) { - desired = PARALLEL_BLOCK | DELAYED_LEAVE; + desired = PARALLEL_PHASE | DELAYED_LEAVE; } else { - desired = PARALLEL_BLOCK | prev; + desired = PARALLEL_PHASE | prev; } } while (!my_state.compare_exchange_strong(prev, desired)); } - void unregister_parallel_block(bool enable_fast_leave) { + void unregister_parallel_phase(bool enable_fast_leave) { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); std::uint64_t prev = my_state.load(std::memory_order_relaxed); std::uint64_t desired{}; do { - if (((prev - PARALLEL_BLOCK) & PARALLEL_BLOCK_MASK) != 0) { - desired = prev - PARALLEL_BLOCK; + if (((prev - PARALLEL_PHASE) & PARALLEL_PHASE_MASK) != 0) { + desired = prev - PARALLEL_PHASE; } else { - desired = enable_fast_leave && (prev - PARALLEL_BLOCK == DELAYED_LEAVE) ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_BLOCK; + desired = enable_fast_leave && (prev - PARALLEL_PHASE == DELAYED_LEAVE) ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_PHASE; } } while (!my_state.compare_exchange_strong(prev, desired)); } - bool should_leave() { + bool is_retention_allowed() { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); std::uint64_t curr = my_state.load(std::memory_order_relaxed); - return curr == FAST_LEAVE || curr == ONE_TIME_FAST_LEAVE; + return curr != FAST_LEAVE && curr != ONE_TIME_FAST_LEAVE; } }; -#endif /* __TBB_PREVIEW_PARALLEL_BLOCK */ +#endif /* __TBB_PREVIEW_PARALLEL_PHASE */ //! The structure of an arena, except the array of slots. /** Separated in order to simplify padding. @@ -317,7 +314,7 @@ struct arena_base : padded { //! Waiting object for external threads that cannot join the arena. concurrent_monitor my_exit_monitors; -#if __TBB_PREVIEW_PARALLEL_BLOCK +#if __TBB_PREVIEW_PARALLEL_PHASE //! Manages state of thread_leave state machine thread_leave_manager my_thread_leave; #endif @@ -359,22 +356,22 @@ class arena: public padded //! Constructor arena(threading_control* control, unsigned max_num_workers, unsigned num_reserved_slots, unsigned priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy wl #endif ); //! Allocate an instance of arena. static arena& allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy wl #endif ); static arena& create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints = d1::constraints{} -#if __TBB_PREVIEW_PARALLEL_BLOCK - , tbb::task_arena::workers_leave wl = tbb::task_arena::workers_leave::delayed +#if __TBB_PREVIEW_PARALLEL_PHASE + , tbb::task_arena::leave_policy wl = tbb::task_arena::leave_policy::automatic #endif ); diff --git a/src/tbb/def/lin32-tbb.def b/src/tbb/def/lin32-tbb.def index a33fabbf16..b71e08e5bb 100644 --- a/src/tbb/def/lin32-tbb.def +++ b/src/tbb/def/lin32-tbb.def @@ -107,8 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb; +_ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEj; +_ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEj; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/src/tbb/def/lin64-tbb.def b/src/tbb/def/lin64-tbb.def index afb9c2e8e0..71be72b678 100644 --- a/src/tbb/def/lin64-tbb.def +++ b/src/tbb/def/lin64-tbb.def @@ -107,8 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb; +_ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEm; +_ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEm; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index e7a38db2de..e66f4ae697 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -58,16 +58,10 @@ class outermost_worker_waiter : public waiter_base { __TBB_ASSERT(t == nullptr, nullptr); if (is_worker_should_leave(slot)) { - if ( -#if __TBB_PREVIEW_PARALLEL_BLOCK - !my_arena.my_thread_leave.should_leave() -#else - !governor::hybrid_cpu() -#endif - ) - { + if (can_worker_be_retained()) { static constexpr std::chrono::microseconds worker_wait_leave_duration(1000); - static_assert(worker_wait_leave_duration > std::chrono::steady_clock::duration(1), "Clock resolution is not enough for measured interval."); + static_assert(worker_wait_leave_duration > std::chrono::steady_clock::duration(1), + "Clock resolution is not enough for measured interval."); for (auto t1 = std::chrono::steady_clock::now(), t2 = t1; std::chrono::duration_cast(t2 - t1) < worker_wait_leave_duration; @@ -77,13 +71,7 @@ class outermost_worker_waiter : public waiter_base { return true; } - if ( -#if __TBB_PREVIEW_PARALLEL_BLOCK - my_arena.my_thread_leave.should_leave() || -#endif - my_arena.my_threading_control->is_any_other_client_active() - ) - { + if (!my_arena.my_thread_leave.is_retention_allowed() || my_arena.my_threading_control->is_any_other_client_active()) { break; } d0::yield(); @@ -113,6 +101,14 @@ class outermost_worker_waiter : public waiter_base { private: using base_type = waiter_base; + bool can_worker_be_retained() { + #if __TBB_PREVIEW_PARALLEL_PHASE + return my_arena.my_thread_leave.is_retention_allowed(); +#else + return !governor::hybrid_cpu(); +#endif + } + bool is_worker_should_leave(arena_slot& slot) const { bool is_top_priority_arena = my_arena.is_top_priority(); bool is_task_pool_empty = slot.task_pool.load(std::memory_order_relaxed) == EmptyTaskPool; diff --git a/test/common/config.h b/test/common/config.h index c83fbf2f40..7ae67a6224 100644 --- a/test/common/config.h +++ b/test/common/config.h @@ -39,8 +39,8 @@ #ifndef TBB_PREVIEW_ISOLATED_TASK_GROUP #define TBB_PREVIEW_ISOLATED_TASK_GROUP 1 #endif -#ifndef TBB_PREVIEW_PARALLEL_BLOCK -#define TBB_PREVIEW_PARALLEL_BLOCK 1 +#ifndef TBB_PREVIEW_PARALLEL_PHASE +#define TBB_PREVIEW_PARALLEL_PHASE 1 #endif #endif diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index ef7a9b791d..156821a70b 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2071,7 +2071,7 @@ TEST_CASE("worker threads occupy slots in correct range") { while (counter < 42) { utils::yield(); } } -#if TBB_PREVIEW_PARALLEL_BLOCK +#if TBB_PREVIEW_PARALLEL_PHASE struct dummy_func { void operator()() const { @@ -2079,8 +2079,8 @@ struct dummy_func { }; template -std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start_block = F1{}, const F2& end_block = F2{}) { - std::size_t num_threads = utils::get_platform_max_threads(); +std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start = F1{}, const F2& end = F2{}) { + std::size_t num_threads = ta.max_concurrency(); std::size_t num_runs = 1000; std::vector longest_start_times; longest_start_times.reserve(num_runs); @@ -2101,27 +2101,25 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start_block = }; for (std::size_t i = 0; i < num_runs; ++i) { - // std::cout << i << std::endl; ta.execute([&] { auto start_time = std::chrono::steady_clock::now(); - start_block(); + start(); tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); - end_block(); + end(); longest_start_times.push_back(get_longest_start(start_time)); }); - // std::this_thread::sleep_for(std::chrono::microseconds(500)); utils::doDummyWork(i*100); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } //! \brief \ref interface \ref requirement -TEST_CASE("Check that workers leave faster with workers_leave::fast") { +TEST_CASE("Check that workers leave faster with leave_policy::fast") { std::size_t num_threads = utils::get_platform_max_threads(); std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_delayed[i] = avg_start_time; @@ -2130,7 +2128,7 @@ TEST_CASE("Check that workers leave faster with workers_leave::fast") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_fast[i] = avg_start_time; @@ -2148,18 +2146,18 @@ TEST_CASE("parallel_block retains workers in task_arena") { std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); for (std::size_t i = 0; i < num_trials; ++i) { - ta.start_parallel_block(); + ta.start_parallel_phase(); auto avg_start_time = measure_avg_start_time(ta); - ta.end_parallel_block(true); + ta.end_parallel_phase(/*with_fast_leave=*/true); avg_start_time_delayed[i] = avg_start_time; } } std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::fast); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_fast[i] = avg_start_time; @@ -2177,7 +2175,7 @@ TEST_CASE("Test one-time fast leave") { std::size_t num_trials = 20; std::vector avg_start_time_delayed(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); for (std::size_t i = 0; i < num_trials; ++i) { auto avg_start_time = measure_avg_start_time(ta); avg_start_time_delayed[i] = avg_start_time; @@ -2186,11 +2184,11 @@ TEST_CASE("Test one-time fast leave") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::vector avg_start_time_fast(num_trials); { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::workers_leave::delayed); - auto start_block = [&ta] { ta.start_parallel_block(); }; - auto end_block = [&ta] { ta.end_parallel_block(true); }; + tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); + auto start_phase = [&ta] { ta.start_parallel_phase(); }; + auto end_phase = [&ta] { ta.end_parallel_phase(/*with_fast_leave=*/true); }; for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta, start_block, end_block); + auto avg_start_time = measure_avg_start_time(ta, start_phase, end_phase); avg_start_time_fast[i] = avg_start_time; } } From 104606c1e3c6d13741b506a909338dd18696e064 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 3 Dec 2024 17:38:05 +0100 Subject: [PATCH 14/35] Fix non-preview compilation Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 7452938899..67574939a5 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -207,7 +207,11 @@ class task_arena_base { , leave_policy lp #endif ) - : my_version_and_traits(default_flags | core_type_support_flag | leave_policy_to_traits(lp)) + : my_version_and_traits(default_flags | core_type_support_flag +#if __TBB_PREVIEW_PARALLEL_PHASE + | leave_policy_to_traits(lp) +#endif + ) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) , my_max_concurrency(max_concurrency) @@ -224,7 +228,11 @@ class task_arena_base { , leave_policy lp #endif ) - : my_version_and_traits(default_flags | core_type_support_flag | leave_policy_to_traits(lp)) + : my_version_and_traits(default_flags | core_type_support_flag +#if __TBB_PREVIEW_PARALLEL_PHASE + | leave_policy_to_traits(lp) +#endif + ) , my_initialization_state(do_once_state::uninitialized) , my_arena(nullptr) , my_max_concurrency(constraints_.max_concurrency) From 597136fab22ffa07b7e41b9ab293b018a19bcd13 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 4 Dec 2024 09:48:19 +0100 Subject: [PATCH 15/35] Change scoped_parallel_phase default parameter, fix entry points for Win Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 2 +- src/tbb/def/win32-tbb.def | 4 ++-- src/tbb/def/win64-tbb.def | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 67574939a5..92a0edd3ca 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -516,7 +516,7 @@ class task_arena : public task_arena_base { task_arena& arena; bool one_time_fast_leave; public: - scoped_parallel_phase(task_arena& ta, bool with_fast_leave = true) + scoped_parallel_phase(task_arena& ta, bool with_fast_leave = false) : arena(ta), one_time_fast_leave(with_fast_leave) { arena.start_parallel_phase(); diff --git a/src/tbb/def/win32-tbb.def b/src/tbb/def/win32-tbb.def index 0b2695c80a..4813495fa5 100644 --- a/src/tbb/def/win32-tbb.def +++ b/src/tbb/def/win32-tbb.def @@ -101,8 +101,8 @@ EXPORTS ?wait@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@@Z ?enqueue@r1@detail@tbb@@YAXAAVtask@d1@23@AAVtask_group_context@523@PAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGABVtask_arena_base@d1@23@@Z -?register_parallel_block@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@@Z -?unregister_parallel_block@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@_N@Z +?register_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z +?unregister_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ diff --git a/src/tbb/def/win64-tbb.def b/src/tbb/def/win64-tbb.def index b68a46da2a..5f417b49da 100644 --- a/src/tbb/def/win64-tbb.def +++ b/src/tbb/def/win64-tbb.def @@ -101,8 +101,8 @@ EXPORTS ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@PEAVtask_arena_base@523@@Z ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@PEAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGAEBVtask_arena_base@d1@23@@Z -?register_parallel_block@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@@Z -?unregister_parallel_block@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@_N@Z +?register_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z +?unregister_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ From 1bc6e3a38cbe0161b70aeca0a0f13ea2d2b0563a Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Mon, 9 Dec 2024 10:32:04 +0100 Subject: [PATCH 16/35] Apply suggestions from code review Co-authored-by: Aleksei Fedotov --- include/oneapi/tbb/task_arena.h | 4 ++-- src/tbb/arena.cpp | 3 ++- src/tbb/arena.h | 6 +++++- src/tbb/waiters.h | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 92a0edd3ca..b780fec2ae 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -503,7 +503,7 @@ class task_arena : public task_arena_base { #if __TBB_PREVIEW_PARALLEL_PHASE void start_parallel_phase() { initialize(); - r1::register_parallel_phase(this, 0); + r1::register_parallel_phase(this, /*reserved*/0); // Trigger worker threads to join arena enqueue([]{}); } @@ -597,7 +597,7 @@ inline void enqueue(F&& f) { #if __TBB_PREVIEW_PARALLEL_PHASE inline void start_parallel_phase() { - r1::register_parallel_phase(nullptr, 0); + r1::register_parallel_phase(nullptr, /*reserved*/0); } inline void end_parallel_phase(bool with_fast_leave) { diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index bdced70b4c..ca254101cd 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -609,7 +609,8 @@ void task_arena_impl::initialize(d1::task_arena_base& ta) { __TBB_ASSERT(ta.my_arena.load(std::memory_order_relaxed) == nullptr, "Arena already initialized"); unsigned priority_level = arena_priority_level(ta.my_priority); threading_control* thr_control = threading_control::register_public_reference(); - arena& a = arena::create(thr_control, unsigned(ta.my_max_concurrency), ta.my_num_reserved_slots, priority_level, arena_constraints + arena& a = arena::create(thr_control, unsigned(ta.my_max_concurrency), ta.my_num_reserved_slots, + priority_level, arena_constraints #if __TBB_PREVIEW_PARALLEL_PHASE , ta.get_leave_policy() #endif diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 351f2fbc8b..a842e032f3 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -196,6 +196,8 @@ class thread_leave_manager { std::uint64_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; my_state.store(platform_policy, std::memory_order_relaxed); } else { + __TBB_ASSERT(wl == tbb::task_arena::leave_policy::fast, + "Was the new value introduced for leave policy?"); my_state.store(FAST_LEAVE, std::memory_order_relaxed); } } @@ -369,7 +371,9 @@ class arena: public padded #endif ); - static arena& create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned arena_priority_level, d1::constraints constraints = d1::constraints{} + static arena& create(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, + unsigned arena_priority_level, + d1::constraints constraints = d1::constraints{} #if __TBB_PREVIEW_PARALLEL_PHASE , tbb::task_arena::leave_policy wl = tbb::task_arena::leave_policy::automatic #endif diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index e66f4ae697..57f48ef20c 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -101,7 +101,7 @@ class outermost_worker_waiter : public waiter_base { private: using base_type = waiter_base; - bool can_worker_be_retained() { + bool is_delayed_leave_enabled() { #if __TBB_PREVIEW_PARALLEL_PHASE return my_arena.my_thread_leave.is_retention_allowed(); #else From 73742f353e3a2c59011e2f20d324e1c5e6e58a6f Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Fri, 6 Dec 2024 14:57:36 +0100 Subject: [PATCH 17/35] Apply comments, change tests Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 4 +- src/tbb/arena.h | 13 ++- src/tbb/waiters.h | 2 +- test/tbb/test_task_arena.cpp | 182 +++++++++++++++++++------------- 4 files changed, 120 insertions(+), 81 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index b780fec2ae..c375e5ad52 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -27,8 +27,6 @@ #include "detail/_task.h" #include "detail/_task_handle.h" -#include "oneapi/tbb/detail/_assert.h" -#include #if __TBB_ARENA_BINDING #include "info.h" @@ -199,7 +197,7 @@ class task_arena_base { enum { default_flags = 0, - core_type_support_flag = 1, + core_type_support_flag = 1 }; task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority diff --git a/src/tbb/arena.h b/src/tbb/arena.h index a842e032f3..6d52b27d09 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -187,7 +187,7 @@ class thread_leave_manager { static const std::uint64_t PARALLEL_PHASE = 1 << 3; // Use 29 bits for the parallel block state + reference counter, // reserve 32 most significant bits. - static const std::uint64_t PARALLEL_PHASE_MASK = ((1LLU << 32) - 1) & (PARALLEL_PHASE - 1); + static const std::uint64_t PARALLEL_PHASE_MASK = ((1LLU << 32) - 1) & ~(PARALLEL_PHASE - 1); std::atomic my_state{0}; public: @@ -210,7 +210,8 @@ class thread_leave_manager { my_state.compare_exchange_strong(curr, DELAYED_LEAVE); } } - + + // Indicate start of parallel phase in the state machine void register_parallel_phase() { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); @@ -218,15 +219,20 @@ class thread_leave_manager { std::uint64_t desired{}; do { if (prev & PARALLEL_PHASE_MASK) { + // The parallel phase is already started, thus simply add a reference to it desired = PARALLEL_PHASE + prev; } else if (prev == ONE_TIME_FAST_LEAVE) { + // State was previously transitioned to "One-time Fast leave", thus + // with the start of new parallel phase, it should be then transitioned to "Delayed leave" desired = PARALLEL_PHASE | DELAYED_LEAVE; } else { + // Transition to "start of parallel phase" state and preserving the default state desired = PARALLEL_PHASE | prev; } } while (!my_state.compare_exchange_strong(prev, desired)); } + // Indicate the end of parallel phase in the state machine void unregister_parallel_phase(bool enable_fast_leave) { __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); @@ -234,8 +240,11 @@ class thread_leave_manager { std::uint64_t desired{}; do { if (((prev - PARALLEL_PHASE) & PARALLEL_PHASE_MASK) != 0) { + // There are still other parallel phases, thus just decrease the reference counter desired = prev - PARALLEL_PHASE; } else { + // We are the last parallel phase, thus transition to the default state + // or to the "One-time Fast leave" state if it was requested desired = enable_fast_leave && (prev - PARALLEL_PHASE == DELAYED_LEAVE) ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_PHASE; } } while (!my_state.compare_exchange_strong(prev, desired)); diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index 57f48ef20c..84f4c30ab5 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -58,7 +58,7 @@ class outermost_worker_waiter : public waiter_base { __TBB_ASSERT(t == nullptr, nullptr); if (is_worker_should_leave(slot)) { - if (can_worker_be_retained()) { + if (is_delayed_leave_enabled()) { static constexpr std::chrono::microseconds worker_wait_leave_duration(1000); static_assert(worker_wait_leave_duration > std::chrono::steady_clock::duration(1), "Clock resolution is not enough for measured interval."); diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 156821a70b..84616a3014 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2113,90 +2113,122 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start = F1{}, return utils::median(longest_start_times.begin(), longest_start_times.end()); } -//! \brief \ref interface \ref requirement -TEST_CASE("Check that workers leave faster with leave_policy::fast") { - std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t num_trials = 20; - std::vector avg_start_time_delayed(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); +template +class start_time_collection_base { + friend Impl; +public: + start_time_collection_base(tbb::task_arena& ta, std::size_t ntrials) : + arena(ta), num_trials(ntrials), average_start_times(ntrials) {} + + std::vector measure() { for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta); - avg_start_time_delayed[i] = avg_start_time; + std::size_t avg_start_time = static_cast(this)->measure_impl(); + average_start_times[i] = avg_start_time; } + return average_start_times; } +protected: + tbb::task_arena& arena; + std::size_t num_trials; + std::vector average_start_times; +}; + +class start_time_collection : public start_time_collection_base { + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + return measure_avg_start_time(arena); + }; +}; + +class start_time_collection_phase_wrapped : public start_time_collection_base { + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + arena.start_parallel_phase(); + auto avg_start_time = measure_avg_start_time(arena); + arena.end_parallel_phase(true); + return avg_start_time; + }; +}; + +class start_time_collection_sequenced_phases: public start_time_collection_base { + using base = start_time_collection_base; + using base::base; + friend base; + + + std::size_t measure_impl() { + auto avg_start_time = measure_avg_start_time(arena, + [this] { + arena.start_parallel_phase(); + }, + [this] { + arena.end_parallel_phase(/*with_fast_leave=*/true); + }); + return avg_start_time; + }; +}; + +template +bool test_start_times(Collection1& collector1, Collection2& collector2) { + auto times1 = collector1.measure(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::vector avg_start_time_fast(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); - for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta); - avg_start_time_fast[i] = avg_start_time; - } - } - std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); - std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; - WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster with delayed leave"); + + auto times2 = collector2.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + + return median1 < median2; } -TEST_CASE("parallel_block retains workers in task_arena") { - std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t num_trials = 20; - std::vector avg_start_time_delayed(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); - for (std::size_t i = 0; i < num_trials; ++i) { - ta.start_parallel_phase(); - auto avg_start_time = measure_avg_start_time(ta); - ta.end_parallel_phase(/*with_fast_leave=*/true); - avg_start_time_delayed[i] = avg_start_time; - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::vector avg_start_time_fast(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast); - for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta); - avg_start_time_fast[i] = avg_start_time; - } - } - std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); - std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; - WARN_MESSAGE(delayed_avg < fast_avg, "Expected workers start new work faster when using parallel_block"); +TEST_CASE("Check that workers leave faster with leave_policy::fast") { + tbb::task_arena ta_automatic_leave { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::automatic + }; + tbb::task_arena ta_fast_leave { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; + start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; + WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + "Expected workers to start new work faster with delayed leave"); +} + +TEST_CASE("parallel_phase retains workers in task_arena") { + tbb::task_arena ta_fast1 { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + tbb::task_arena ta_fast2 { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; + start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; + WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + "Expected workers start new work faster when using parallel_phase"); } TEST_CASE("Test one-time fast leave") { - std::size_t num_threads = utils::get_platform_max_threads(); - std::size_t num_trials = 20; - std::vector avg_start_time_delayed(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); - for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta); - avg_start_time_delayed[i] = avg_start_time; - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::vector avg_start_time_fast(num_trials); - { - tbb::task_arena ta((int)num_threads, 1, tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::automatic); - auto start_phase = [&ta] { ta.start_parallel_phase(); }; - auto end_phase = [&ta] { ta.end_parallel_phase(/*with_fast_leave=*/true); }; - for (std::size_t i = 0; i < num_trials; ++i) { - auto avg_start_time = measure_avg_start_time(ta, start_phase, end_phase); - avg_start_time_fast[i] = avg_start_time; - } - } - std::sort(avg_start_time_delayed.begin(), avg_start_time_delayed.end()); - std::sort(avg_start_time_fast.begin(), avg_start_time_fast.end()); - auto delayed_avg = std::accumulate(avg_start_time_delayed.begin(), avg_start_time_delayed.begin() + int(num_trials * 0.9), 0ull) / num_trials; - auto fast_avg = std::accumulate(avg_start_time_fast.begin(), avg_start_time_fast.begin() + int(num_trials * 0.9), 0ull) / num_trials; - WARN_MESSAGE(delayed_avg < fast_avg, "Expected one-time fast leave setting to slow workers to start new work"); + tbb::task_arena ta1{}; + tbb::task_arena ta2{}; + start_time_collection st_collector1{ta1, /*num_trials=*/20}; + start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/20}; + WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + "Expected one-time fast leave setting to slow workers to start new work"); } #endif From ba5b922858e53fc31119a37d279a8333f4ad10eb Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 10 Dec 2024 10:52:24 +0100 Subject: [PATCH 18/35] Correct mac64 exports Signed-off-by: Isaev, Ilya --- src/tbb/def/mac64-tbb.def | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbb/def/mac64-tbb.def b/src/tbb/def/mac64-tbb.def index 403259eec1..1895dbdbbb 100644 --- a/src/tbb/def/mac64-tbb.def +++ b/src/tbb/def/mac64-tbb.def @@ -109,8 +109,8 @@ __ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE __ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE __ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE __ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE -__ZN3tbb6detail2r123register_parallel_blockERNS0_2d115task_arena_baseE -__ZN3tbb6detail2r125unregister_parallel_blockERNS0_2d115task_arena_baseEb +__ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEm +__ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEm # System topology parsing and threads pinning (governor.cpp) __ZN3tbb6detail2r115numa_node_countEv From 7311cecd640124f5c4b768476b9da442f7080a15 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Fri, 13 Dec 2024 10:26:28 +0100 Subject: [PATCH 19/35] Improve test reporting, move thread demand into internals Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 10 ++++----- src/tbb/arena.cpp | 3 ++- test/tbb/test_task_arena.cpp | 39 +++++++++++++++++---------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index c375e5ad52..3f0bc5a611 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.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. @@ -502,12 +502,11 @@ class task_arena : public task_arena_base { void start_parallel_phase() { initialize(); r1::register_parallel_phase(this, /*reserved*/0); - // Trigger worker threads to join arena - enqueue([]{}); } void end_parallel_phase(bool with_fast_leave = false) { __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); - r1::unregister_parallel_phase(this, with_fast_leave); + // It is guaranteed by the standard that conversion of boolean to integral type will result in either 0 or 1 + r1::unregister_parallel_phase(this, static_cast(with_fast_leave)); } class scoped_parallel_phase { @@ -599,7 +598,8 @@ inline void start_parallel_phase() { } inline void end_parallel_phase(bool with_fast_leave) { - r1::unregister_parallel_phase(nullptr, with_fast_leave); + // It is guaranteed by the standard that conversion of boolean to integral type will result in either 0 or 1 + r1::unregister_parallel_phase(nullptr, static_cast(with_fast_leave)); } #endif diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index ca254101cd..7e5bf44cbb 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -572,7 +572,7 @@ void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base* ta, std::u } void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { - task_arena_impl::unregister_parallel_phase(ta, flags); + task_arena_impl::unregister_parallel_phase(ta, static_cast(flags)); } void task_arena_impl::initialize(d1::task_arena_base& ta) { @@ -926,6 +926,7 @@ void task_arena_impl::register_parallel_phase(d1::task_arena_base* ta) { arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; __TBB_ASSERT(a, nullptr); a->my_thread_leave.register_parallel_phase(); + a->advertise_new_work(); } void task_arena_impl::unregister_parallel_phase(d1::task_arena_base* ta, bool with_fast_leave) { diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 84616a3014..ddca1e016c 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2079,7 +2079,7 @@ struct dummy_func { }; template -std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start = F1{}, const F2& end = F2{}) { +std::size_t measure_median_start_time(tbb::task_arena& ta, const F1& start = F1{}, const F2& end = F2{}) { std::size_t num_threads = ta.max_concurrency(); std::size_t num_runs = 1000; std::vector longest_start_times; @@ -2108,7 +2108,7 @@ std::size_t measure_avg_start_time(tbb::task_arena& ta, const F1& start = F1{}, end(); longest_start_times.push_back(get_longest_start(start_time)); }); - utils::doDummyWork(i*100); + utils::doDummyWork(i*1000); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -2118,19 +2118,19 @@ class start_time_collection_base { friend Impl; public: start_time_collection_base(tbb::task_arena& ta, std::size_t ntrials) : - arena(ta), num_trials(ntrials), average_start_times(ntrials) {} + arena(ta), num_trials(ntrials), start_times(ntrials) {} std::vector measure() { for (std::size_t i = 0; i < num_trials; ++i) { - std::size_t avg_start_time = static_cast(this)->measure_impl(); - average_start_times[i] = avg_start_time; + std::size_t median_start_time = static_cast(this)->measure_impl(); + start_times[i] = median_start_time; } - return average_start_times; + return start_times; } protected: tbb::task_arena& arena; std::size_t num_trials; - std::vector average_start_times; + std::vector start_times; }; class start_time_collection : public start_time_collection_base { @@ -2139,7 +2139,7 @@ class start_time_collection : public start_time_collection_base -bool test_start_times(Collection1& collector1, Collection2& collector2) { +std::pair test_start_times(Collection1& collector1, Collection2& collector2) { auto times1 = collector1.measure(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - auto times2 = collector2.measure(); auto median1 = utils::median(times1.begin(), times1.end()); auto median2 = utils::median(times2.begin(), times2.end()); - return median1 < median2; + return { median1, median2 }; } TEST_CASE("Check that workers leave faster with leave_policy::fast") { @@ -2201,7 +2199,8 @@ TEST_CASE("Check that workers leave faster with leave_policy::fast") { }; start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; - WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + auto times = test_start_times(st_collector1, st_collector2); + WARN_MESSAGE(times.first < times.second, "Expected workers to start new work faster with delayed leave"); } @@ -2218,7 +2217,8 @@ TEST_CASE("parallel_phase retains workers in task_arena") { }; start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; - WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + auto times = test_start_times(st_collector1, st_collector2); + WARN_MESSAGE(times.first < times.second, "Expected workers start new work faster when using parallel_phase"); } @@ -2227,7 +2227,8 @@ TEST_CASE("Test one-time fast leave") { tbb::task_arena ta2{}; start_time_collection st_collector1{ta1, /*num_trials=*/20}; start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/20}; - WARN_MESSAGE(test_start_times(st_collector1, st_collector2), + auto times = test_start_times(st_collector1, st_collector2); + WARN_MESSAGE(times.first < times.second, "Expected one-time fast leave setting to slow workers to start new work"); } From 1f22bc649962f4c8e5c8e21711146165bb03888b Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Fri, 13 Dec 2024 15:58:55 +0100 Subject: [PATCH 20/35] Extend testing suite with scoped phases and test with this_task_arena Signed-off-by: Isaev, Ilya --- test/tbb/test_task_arena.cpp | 171 ++++++++++++++++++++++++++--------- 1 file changed, 128 insertions(+), 43 deletions(-) diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index ddca1e016c..78f1a1bd68 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2079,8 +2079,8 @@ struct dummy_func { }; template -std::size_t measure_median_start_time(tbb::task_arena& ta, const F1& start = F1{}, const F2& end = F2{}) { - std::size_t num_threads = ta.max_concurrency(); +std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{}, const F2& end = F2{}) { + std::size_t num_threads = ta ? ta->max_concurrency() : tbb::this_task_arena::max_concurrency(); std::size_t num_runs = 1000; std::vector longest_start_times; longest_start_times.reserve(num_runs); @@ -2100,15 +2100,21 @@ std::size_t measure_median_start_time(tbb::task_arena& ta, const F1& start = F1{ return longest_time; }; + auto work = [&] { + auto start_time = std::chrono::steady_clock::now(); + start(); + tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + end(); + longest_start_times.push_back(get_longest_start(start_time)); + }; + for (std::size_t i = 0; i < num_runs; ++i) { - ta.execute([&] { - auto start_time = std::chrono::steady_clock::now(); - start(); - tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); - end(); - longest_start_times.push_back(get_longest_start(start_time)); - }); - utils::doDummyWork(i*1000); + if (ta) { + ta->execute(work); + } else { + work(); + } + utils::doDummyWork(i*500); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -2118,7 +2124,10 @@ class start_time_collection_base { friend Impl; public: start_time_collection_base(tbb::task_arena& ta, std::size_t ntrials) : - arena(ta), num_trials(ntrials), start_times(ntrials) {} + arena(&ta), num_trials(ntrials), start_times(ntrials) {} + + explicit start_time_collection_base(std::size_t ntrials) : + arena(nullptr), num_trials(ntrials), start_times(ntrials) {} std::vector measure() { for (std::size_t i = 0; i < num_trials; ++i) { @@ -2128,7 +2137,7 @@ class start_time_collection_base { return start_times; } protected: - tbb::task_arena& arena; + tbb::task_arena* arena; std::size_t num_trials; std::vector start_times; }; @@ -2143,49 +2152,78 @@ class start_time_collection : public start_time_collection_base { +class start_time_collection_phase_wrapped + : public start_time_collection_base +{ using base = start_time_collection_base; using base::base; friend base; std::size_t measure_impl() { - arena.start_parallel_phase(); + arena->start_parallel_phase(); auto median_start_time = measure_median_start_time(arena); - arena.end_parallel_phase(true); + arena->end_parallel_phase(true); return median_start_time; }; }; -class start_time_collection_sequenced_phases: public start_time_collection_base { - using base = start_time_collection_base; +class start_time_collection_scoped_phase_wrapped + : public start_time_collection_base +{ + using base = start_time_collection_base; using base::base; friend base; - std::size_t measure_impl() { - auto median_start_time = measure_median_start_time(arena, - [this] { - arena.start_parallel_phase(); - }, - [this] { - arena.end_parallel_phase(/*with_fast_leave=*/true); - }); + tbb::task_arena::scoped_parallel_phase phase{*arena}; + auto median_start_time = measure_median_start_time(arena); return median_start_time; }; }; -template -std::pair test_start_times(Collection1& collector1, Collection2& collector2) { - auto times1 = collector1.measure(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - auto times2 = collector2.measure(); +class start_time_collection_sequenced_phases + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; - auto median1 = utils::median(times1.begin(), times1.end()); - auto median2 = utils::median(times2.begin(), times2.end()); + std::size_t measure_impl() { + std::size_t median_start_time; + if (arena) { + median_start_time = measure_median_start_time(arena, + [this] { arena->start_parallel_phase(); }, + [this] { arena->end_parallel_phase(/*with_fast_leave=*/true); }); + } else { + median_start_time = measure_median_start_time(arena, + [] { tbb::this_task_arena::start_parallel_phase(); }, + [] { tbb::this_task_arena::end_parallel_phase(/*with_fast_leave=*/true); }); + } + return median_start_time; + }; +}; - return { median1, median2 }; -} +class start_time_collection_sequenced_scoped_phases + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + tbb::task_arena::scoped_parallel_phase* phase = nullptr; + auto median_start_time = measure_median_start_time(arena, + [this, &phase] { + phase = new tbb::task_arena::scoped_parallel_phase{*arena, /*with_fast_leave=*/true}; + }, + [&phase] { + delete phase; + }); + return median_start_time; + }; +}; +//! \brief \ref interface \ref requirement TEST_CASE("Check that workers leave faster with leave_policy::fast") { tbb::task_arena ta_automatic_leave { tbb::task_arena::automatic, 1, @@ -2199,12 +2237,19 @@ TEST_CASE("Check that workers leave faster with leave_policy::fast") { }; start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; - auto times = test_start_times(st_collector1, st_collector2); - WARN_MESSAGE(times.first < times.second, + + auto times_automatic = st_collector1.measure(); + auto times_fast = st_collector2.measure(); + + auto median_automatic = utils::median(times_automatic.begin(), times_automatic.end()); + auto median_fast = utils::median(times_fast.begin(), times_fast.end()); + + WARN_MESSAGE(median_automatic < median_fast, "Expected workers to start new work faster with delayed leave"); } -TEST_CASE("parallel_phase retains workers in task_arena") { +//! \brief \ref interface \ref requirement +TEST_CASE("Parallel Phase retains workers in task_arena") { tbb::task_arena ta_fast1 { tbb::task_arena::automatic, 1, tbb::task_arena::priority::normal, @@ -2216,19 +2261,59 @@ TEST_CASE("parallel_phase retains workers in task_arena") { tbb::task_arena::leave_policy::fast }; start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; + start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/20}; start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; - auto times = test_start_times(st_collector1, st_collector2); - WARN_MESSAGE(times.first < times.second, + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + auto times_scoped = st_collector_scoped.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); + + WARN_MESSAGE(median1 < median2, "Expected workers start new work faster when using parallel_phase"); + + WARN_MESSAGE(median_scoped < median2, + "Expected workers start new work faster when using scoped parallel_phase"); } +//! \brief \ref interface \ref requirement TEST_CASE("Test one-time fast leave") { tbb::task_arena ta1{}; tbb::task_arena ta2{}; - start_time_collection st_collector1{ta1, /*num_trials=*/20}; - start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/20}; - auto times = test_start_times(st_collector1, st_collector2); - WARN_MESSAGE(times.first < times.second, + start_time_collection st_collector1{ta1, /*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/10}; + start_time_collection_sequenced_scoped_phases st_collector_scoped{ta2, /*num_trials=*/10}; + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + auto times_scoped = st_collector_scoped.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); + + WARN_MESSAGE(median1 < median2, + "Expected one-time fast leave setting to slow workers to start new work"); + + WARN_MESSAGE(median1 < median_scoped, + "Expected one-time fast leave setting to slow workers to start new work"); +} + +//! \brief \ref interface \ref requirement +TEST_CASE("Test parallel phase with this_task_arena") { + start_time_collection st_collector1{/*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10}; + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + + WARN_MESSAGE(median1 < median2, "Expected one-time fast leave setting to slow workers to start new work"); } From fd4a24f4d274e82a0774592a49683540f680da85 Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Tue, 17 Dec 2024 09:18:53 +0100 Subject: [PATCH 21/35] Apply suggestions from code review Co-authored-by: Aleksei Fedotov --- src/tbb/arena.cpp | 4 ++-- src/tbb/arena.h | 2 +- src/tbb/waiters.h | 2 +- test/tbb/test_task_arena.cpp | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 7e5bf44cbb..1f187a4589 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -291,7 +291,7 @@ arena::arena(threading_control* control, unsigned num_slots, unsigned num_reserv arena& arena::allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level #if __TBB_PREVIEW_PARALLEL_PHASE - , tbb::task_arena::leave_policy wl + , tbb::task_arena::leave_policy lp #endif ) { @@ -359,7 +359,7 @@ bool arena::has_enqueued_tasks() { void arena::request_workers(int mandatory_delta, int workers_delta, bool wakeup_threads) { #if __TBB_PREVIEW_PARALLEL_PHASE - my_thread_leave.restore_state_if_needed(); + my_thread_leave.restore_default_policy_if_needed(); #endif my_threading_control->adjust_demand(my_tc_client, mandatory_delta, workers_delta); diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 6d52b27d09..2cf81dcaf2 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -220,7 +220,7 @@ class thread_leave_manager { do { if (prev & PARALLEL_PHASE_MASK) { // The parallel phase is already started, thus simply add a reference to it - desired = PARALLEL_PHASE + prev; + desired = prev + PARALLEL_PHASE; } else if (prev == ONE_TIME_FAST_LEAVE) { // State was previously transitioned to "One-time Fast leave", thus // with the start of new parallel phase, it should be then transitioned to "Delayed leave" diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index 84f4c30ab5..a217933b7d 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -102,7 +102,7 @@ class outermost_worker_waiter : public waiter_base { using base_type = waiter_base; bool is_delayed_leave_enabled() { - #if __TBB_PREVIEW_PARALLEL_PHASE +#if __TBB_PREVIEW_PARALLEL_PHASE return my_arena.my_thread_leave.is_retention_allowed(); #else return !governor::hybrid_cpu(); diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 78f1a1bd68..7033ffbe94 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2095,7 +2095,8 @@ std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{ auto get_longest_start = [&] (std::chrono::steady_clock::time_point start_time) { std::size_t longest_time = 0; for (auto& time : start_times) { - longest_time = std::max(longest_time, (std::size_t)std::chrono::duration_cast(time - start_time).count()); + auto diff = std::chrono::duration_cast(time - start_time); + longest_time = std::max(longest_time, std::size_t(diff.count())); } return longest_time; }; @@ -2162,7 +2163,7 @@ class start_time_collection_phase_wrapped std::size_t measure_impl() { arena->start_parallel_phase(); auto median_start_time = measure_median_start_time(arena); - arena->end_parallel_phase(true); + arena->end_parallel_phase(/*with_fast_leave*/true); return median_start_time; }; }; From 77985c0d210caf5d1c1d5a38585acb609c99e95b Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 17 Dec 2024 12:16:44 +0100 Subject: [PATCH 22/35] Simplify state transition logic Signed-off-by: Isaev, Ilya --- src/tbb/arena.cpp | 3 +-- src/tbb/arena.h | 34 +++++++++++++--------------------- test/tbb/test_task_arena.cpp | 2 -- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 1f187a4589..1f37ba9bf5 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -14,7 +14,6 @@ limitations under the License. */ -#include "oneapi/tbb/detail/_assert.h" #include "task_dispatcher.h" #include "governor.h" #include "threading_control.h" @@ -306,7 +305,7 @@ arena& arena::allocate_arena(threading_control* control, unsigned num_slots, uns return *new( storage + num_arena_slots(num_slots, num_reserved_slots) * sizeof(mail_outbox) ) arena(control, num_slots, num_reserved_slots, priority_level #if __TBB_PREVIEW_PARALLEL_PHASE - , wl + , lp #endif ); } diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 2cf81dcaf2..ec1cf83df7 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -185,9 +185,6 @@ class thread_leave_manager { static const std::uint64_t ONE_TIME_FAST_LEAVE = 1 << 1; static const std::uint64_t DELAYED_LEAVE = 1 << 2; static const std::uint64_t PARALLEL_PHASE = 1 << 3; - // Use 29 bits for the parallel block state + reference counter, - // reserve 32 most significant bits. - static const std::uint64_t PARALLEL_PHASE_MASK = ((1LLU << 32) - 1) & ~(PARALLEL_PHASE - 1); std::atomic my_state{0}; public: @@ -202,7 +199,7 @@ class thread_leave_manager { } } - void restore_state_if_needed() { + void restore_default_policy_if_needed() { std::uint64_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { // Potentially can override decision of the parallel block from future epoch @@ -218,17 +215,16 @@ class thread_leave_manager { std::uint64_t prev = my_state.load(std::memory_order_relaxed); std::uint64_t desired{}; do { - if (prev & PARALLEL_PHASE_MASK) { - // The parallel phase is already started, thus simply add a reference to it - desired = prev + PARALLEL_PHASE; - } else if (prev == ONE_TIME_FAST_LEAVE) { - // State was previously transitioned to "One-time Fast leave", thus - // with the start of new parallel phase, it should be then transitioned to "Delayed leave" - desired = PARALLEL_PHASE | DELAYED_LEAVE; - } else { - // Transition to "start of parallel phase" state and preserving the default state - desired = PARALLEL_PHASE | prev; + // Need to add a reference for this start of a parallel phase, preserving the leave + // policy. Except for the case when one time fast leave was requested at the end of a + // previous phase. + desired = prev; + if (prev == ONE_TIME_FAST_LEAVE) { + // State was previously transitioned to "One-time Fast leave", thus with the start + // of new parallel phase, it should be transitioned to "Delayed leave" + desired = DELAYED_LEAVE; } + desired += PARALLEL_PHASE; // Take into account this start of a parallel phase } while (!my_state.compare_exchange_strong(prev, desired)); } @@ -239,13 +235,9 @@ class thread_leave_manager { std::uint64_t prev = my_state.load(std::memory_order_relaxed); std::uint64_t desired{}; do { - if (((prev - PARALLEL_PHASE) & PARALLEL_PHASE_MASK) != 0) { - // There are still other parallel phases, thus just decrease the reference counter - desired = prev - PARALLEL_PHASE; - } else { - // We are the last parallel phase, thus transition to the default state - // or to the "One-time Fast leave" state if it was requested - desired = enable_fast_leave && (prev - PARALLEL_PHASE == DELAYED_LEAVE) ? ONE_TIME_FAST_LEAVE : prev - PARALLEL_PHASE; + desired = prev - PARALLEL_PHASE; // Mark the end of this phase in reference counter + if (enable_fast_leave && /*it was the last parallel phase*/desired == DELAYED_LEAVE) { + desired = ONE_TIME_FAST_LEAVE; } } while (!my_state.compare_exchange_strong(prev, desired)); } diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index 7033ffbe94..c89cdebff4 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -14,7 +14,6 @@ limitations under the License. */ -#include "common/dummy_body.h" #include "common/test.h" #define __TBB_EXTRA_DEBUG 1 @@ -42,7 +41,6 @@ #include #include #include -#include //#include "harness_fp.h" From 1a6b73c6f0f08d3b128fa5b0be667de971b7dddf Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 17 Dec 2024 12:20:27 +0100 Subject: [PATCH 23/35] Align leave_policy variables names across code Signed-off-by: Isaev, Ilya --- src/tbb/arena.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index ec1cf83df7..fcc6795109 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -188,12 +188,12 @@ class thread_leave_manager { std::atomic my_state{0}; public: - void set_initial_state(tbb::task_arena::leave_policy wl) { - if (wl == tbb::task_arena::leave_policy::automatic) { + void set_initial_state(tbb::task_arena::leave_policy lp) { + if (lp == tbb::task_arena::leave_policy::automatic) { std::uint64_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; my_state.store(platform_policy, std::memory_order_relaxed); } else { - __TBB_ASSERT(wl == tbb::task_arena::leave_policy::fast, + __TBB_ASSERT(lp == tbb::task_arena::leave_policy::fast, "Was the new value introduced for leave policy?"); my_state.store(FAST_LEAVE, std::memory_order_relaxed); } From 398d16b7e810fee3613c62d95781475b4bc778a2 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 17 Dec 2024 16:04:20 +0100 Subject: [PATCH 24/35] Move parallel phase tests into a separate file Signed-off-by: Isaev, Ilya --- test/CMakeLists.txt | 1 + test/tbb/test_parallel_phase.cpp | 254 +++++++++++++++++++++++++++++++ test/tbb/test_task_arena.cpp | 249 ------------------------------ 3 files changed, 255 insertions(+), 249 deletions(-) create mode 100644 test/tbb/test_parallel_phase.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0ab4d7e8c8..d308dc2367 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -416,6 +416,7 @@ if (TARGET TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_task_group DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_concurrent_hash_map DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_task_arena DEPENDENCIES TBB::tbb) + tbb_add_test(SUBDIR tbb NAME test_parallel_phase DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_enumerable_thread_specific DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_concurrent_queue DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR tbb NAME test_resumable_tasks DEPENDENCIES TBB::tbb) diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp new file mode 100644 index 0000000000..df6119bfed --- /dev/null +++ b/test/tbb/test_parallel_phase.cpp @@ -0,0 +1,254 @@ +#define TBB_PREVIEW_PARALLEL_PHASE 1 + +#include "common/test.h" +#include "common/utils.h" +#include "common/spin_barrier.h" + +#include "tbb/task_arena.h" +#include "tbb/parallel_for.h" + +struct dummy_func { + void operator()() const { + } +}; + +template +std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{}, const F2& end = F2{}) { + std::size_t num_threads = ta ? ta->max_concurrency() : tbb::this_task_arena::max_concurrency(); + std::size_t num_runs = 1000; + std::vector longest_start_times; + longest_start_times.reserve(num_runs); + + std::vector start_times(num_threads); + utils::SpinBarrier barrier(num_threads); + auto measure_start_time = [&] (std::size_t) { + start_times[tbb::this_task_arena::current_thread_index()] = std::chrono::steady_clock::now(); + barrier.wait(); + }; + + auto get_longest_start = [&] (std::chrono::steady_clock::time_point start_time) { + std::size_t longest_time = 0; + for (auto& time : start_times) { + auto diff = std::chrono::duration_cast(time - start_time); + longest_time = std::max(longest_time, std::size_t(diff.count())); + } + return longest_time; + }; + + auto work = [&] { + auto start_time = std::chrono::steady_clock::now(); + start(); + tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); + end(); + longest_start_times.push_back(get_longest_start(start_time)); + }; + + for (std::size_t i = 0; i < num_runs; ++i) { + if (ta) { + ta->execute(work); + } else { + work(); + } + utils::doDummyWork(i*500); + } + return utils::median(longest_start_times.begin(), longest_start_times.end()); +} + +template +class start_time_collection_base { + friend Impl; +public: + start_time_collection_base(tbb::task_arena& ta, std::size_t ntrials) : + arena(&ta), num_trials(ntrials), start_times(ntrials) {} + + explicit start_time_collection_base(std::size_t ntrials) : + arena(nullptr), num_trials(ntrials), start_times(ntrials) {} + + std::vector measure() { + for (std::size_t i = 0; i < num_trials; ++i) { + std::size_t median_start_time = static_cast(this)->measure_impl(); + start_times[i] = median_start_time; + } + return start_times; + } +protected: + tbb::task_arena* arena; + std::size_t num_trials; + std::vector start_times; +}; + +class start_time_collection : public start_time_collection_base { + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + return measure_median_start_time(arena); + }; +}; + +class start_time_collection_phase_wrapped + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + arena->start_parallel_phase(); + auto median_start_time = measure_median_start_time(arena); + arena->end_parallel_phase(/*with_fast_leave*/true); + return median_start_time; + }; +}; + +class start_time_collection_scoped_phase_wrapped + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + tbb::task_arena::scoped_parallel_phase phase{*arena}; + auto median_start_time = measure_median_start_time(arena); + return median_start_time; + }; +}; + +class start_time_collection_sequenced_phases + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + std::size_t median_start_time; + if (arena) { + median_start_time = measure_median_start_time(arena, + [this] { arena->start_parallel_phase(); }, + [this] { arena->end_parallel_phase(/*with_fast_leave=*/true); }); + } else { + median_start_time = measure_median_start_time(arena, + [] { tbb::this_task_arena::start_parallel_phase(); }, + [] { tbb::this_task_arena::end_parallel_phase(/*with_fast_leave=*/true); }); + } + return median_start_time; + }; +}; + +class start_time_collection_sequenced_scoped_phases + : public start_time_collection_base +{ + using base = start_time_collection_base; + using base::base; + friend base; + + std::size_t measure_impl() { + tbb::task_arena::scoped_parallel_phase* phase = nullptr; + auto median_start_time = measure_median_start_time(arena, + [this, &phase] { + phase = new tbb::task_arena::scoped_parallel_phase{*arena, /*with_fast_leave=*/true}; + }, + [&phase] { + delete phase; + }); + return median_start_time; + }; +}; + +//! \brief \ref interface \ref requirement +TEST_CASE("Check that workers leave faster with leave_policy::fast") { + tbb::task_arena ta_automatic_leave { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::automatic + }; + tbb::task_arena ta_fast_leave { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; + start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; + + auto times_automatic = st_collector1.measure(); + auto times_fast = st_collector2.measure(); + + auto median_automatic = utils::median(times_automatic.begin(), times_automatic.end()); + auto median_fast = utils::median(times_fast.begin(), times_fast.end()); + + WARN_MESSAGE(median_automatic < median_fast, + "Expected workers to start new work faster with delayed leave"); +} + +//! \brief \ref interface \ref requirement +TEST_CASE("Parallel Phase retains workers in task_arena") { + tbb::task_arena ta_fast1 { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + tbb::task_arena ta_fast2 { + tbb::task_arena::automatic, 1, + tbb::task_arena::priority::normal, + tbb::task_arena::leave_policy::fast + }; + start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; + start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/20}; + start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + auto times_scoped = st_collector_scoped.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); + + WARN_MESSAGE(median1 < median2, + "Expected workers start new work faster when using parallel_phase"); + + WARN_MESSAGE(median_scoped < median2, + "Expected workers start new work faster when using scoped parallel_phase"); +} + +//! \brief \ref interface \ref requirement +TEST_CASE("Test one-time fast leave") { + tbb::task_arena ta1{}; + tbb::task_arena ta2{}; + start_time_collection st_collector1{ta1, /*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/10}; + start_time_collection_sequenced_scoped_phases st_collector_scoped{ta2, /*num_trials=*/10}; + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + auto times_scoped = st_collector_scoped.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); + + WARN_MESSAGE(median1 < median2, + "Expected one-time fast leave setting to slow workers to start new work"); + + WARN_MESSAGE(median1 < median_scoped, + "Expected one-time fast leave setting to slow workers to start new work"); +} + +//! \brief \ref interface \ref requirement +TEST_CASE("Test parallel phase with this_task_arena") { + start_time_collection st_collector1{/*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10}; + + auto times1 = st_collector1.measure(); + auto times2 = st_collector2.measure(); + + auto median1 = utils::median(times1.begin(), times1.end()); + auto median2 = utils::median(times2.begin(), times2.end()); + + WARN_MESSAGE(median1 < median2, + "Expected one-time fast leave setting to slow workers to start new work"); +} + diff --git a/test/tbb/test_task_arena.cpp b/test/tbb/test_task_arena.cpp index c89cdebff4..6bd93d4c0e 100644 --- a/test/tbb/test_task_arena.cpp +++ b/test/tbb/test_task_arena.cpp @@ -2068,252 +2068,3 @@ TEST_CASE("worker threads occupy slots in correct range") { while (counter < 42) { utils::yield(); } } - -#if TBB_PREVIEW_PARALLEL_PHASE - -struct dummy_func { - void operator()() const { - } -}; - -template -std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{}, const F2& end = F2{}) { - std::size_t num_threads = ta ? ta->max_concurrency() : tbb::this_task_arena::max_concurrency(); - std::size_t num_runs = 1000; - std::vector longest_start_times; - longest_start_times.reserve(num_runs); - - std::vector start_times(num_threads); - utils::SpinBarrier barrier(num_threads); - auto measure_start_time = [&] (std::size_t) { - start_times[tbb::this_task_arena::current_thread_index()] = std::chrono::steady_clock::now(); - barrier.wait(); - }; - - auto get_longest_start = [&] (std::chrono::steady_clock::time_point start_time) { - std::size_t longest_time = 0; - for (auto& time : start_times) { - auto diff = std::chrono::duration_cast(time - start_time); - longest_time = std::max(longest_time, std::size_t(diff.count())); - } - return longest_time; - }; - - auto work = [&] { - auto start_time = std::chrono::steady_clock::now(); - start(); - tbb::parallel_for(std::size_t(0), num_threads, measure_start_time, tbb::static_partitioner{}); - end(); - longest_start_times.push_back(get_longest_start(start_time)); - }; - - for (std::size_t i = 0; i < num_runs; ++i) { - if (ta) { - ta->execute(work); - } else { - work(); - } - utils::doDummyWork(i*500); - } - return utils::median(longest_start_times.begin(), longest_start_times.end()); -} - -template -class start_time_collection_base { - friend Impl; -public: - start_time_collection_base(tbb::task_arena& ta, std::size_t ntrials) : - arena(&ta), num_trials(ntrials), start_times(ntrials) {} - - explicit start_time_collection_base(std::size_t ntrials) : - arena(nullptr), num_trials(ntrials), start_times(ntrials) {} - - std::vector measure() { - for (std::size_t i = 0; i < num_trials; ++i) { - std::size_t median_start_time = static_cast(this)->measure_impl(); - start_times[i] = median_start_time; - } - return start_times; - } -protected: - tbb::task_arena* arena; - std::size_t num_trials; - std::vector start_times; -}; - -class start_time_collection : public start_time_collection_base { - using base = start_time_collection_base; - using base::base; - friend base; - - std::size_t measure_impl() { - return measure_median_start_time(arena); - }; -}; - -class start_time_collection_phase_wrapped - : public start_time_collection_base -{ - using base = start_time_collection_base; - using base::base; - friend base; - - std::size_t measure_impl() { - arena->start_parallel_phase(); - auto median_start_time = measure_median_start_time(arena); - arena->end_parallel_phase(/*with_fast_leave*/true); - return median_start_time; - }; -}; - -class start_time_collection_scoped_phase_wrapped - : public start_time_collection_base -{ - using base = start_time_collection_base; - using base::base; - friend base; - - std::size_t measure_impl() { - tbb::task_arena::scoped_parallel_phase phase{*arena}; - auto median_start_time = measure_median_start_time(arena); - return median_start_time; - }; -}; - -class start_time_collection_sequenced_phases - : public start_time_collection_base -{ - using base = start_time_collection_base; - using base::base; - friend base; - - std::size_t measure_impl() { - std::size_t median_start_time; - if (arena) { - median_start_time = measure_median_start_time(arena, - [this] { arena->start_parallel_phase(); }, - [this] { arena->end_parallel_phase(/*with_fast_leave=*/true); }); - } else { - median_start_time = measure_median_start_time(arena, - [] { tbb::this_task_arena::start_parallel_phase(); }, - [] { tbb::this_task_arena::end_parallel_phase(/*with_fast_leave=*/true); }); - } - return median_start_time; - }; -}; - -class start_time_collection_sequenced_scoped_phases - : public start_time_collection_base -{ - using base = start_time_collection_base; - using base::base; - friend base; - - std::size_t measure_impl() { - tbb::task_arena::scoped_parallel_phase* phase = nullptr; - auto median_start_time = measure_median_start_time(arena, - [this, &phase] { - phase = new tbb::task_arena::scoped_parallel_phase{*arena, /*with_fast_leave=*/true}; - }, - [&phase] { - delete phase; - }); - return median_start_time; - }; -}; - -//! \brief \ref interface \ref requirement -TEST_CASE("Check that workers leave faster with leave_policy::fast") { - tbb::task_arena ta_automatic_leave { - tbb::task_arena::automatic, 1, - tbb::task_arena::priority::normal, - tbb::task_arena::leave_policy::automatic - }; - tbb::task_arena ta_fast_leave { - tbb::task_arena::automatic, 1, - tbb::task_arena::priority::normal, - tbb::task_arena::leave_policy::fast - }; - start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; - start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; - - auto times_automatic = st_collector1.measure(); - auto times_fast = st_collector2.measure(); - - auto median_automatic = utils::median(times_automatic.begin(), times_automatic.end()); - auto median_fast = utils::median(times_fast.begin(), times_fast.end()); - - WARN_MESSAGE(median_automatic < median_fast, - "Expected workers to start new work faster with delayed leave"); -} - -//! \brief \ref interface \ref requirement -TEST_CASE("Parallel Phase retains workers in task_arena") { - tbb::task_arena ta_fast1 { - tbb::task_arena::automatic, 1, - tbb::task_arena::priority::normal, - tbb::task_arena::leave_policy::fast - }; - tbb::task_arena ta_fast2 { - tbb::task_arena::automatic, 1, - tbb::task_arena::priority::normal, - tbb::task_arena::leave_policy::fast - }; - start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; - start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/20}; - start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; - - auto times1 = st_collector1.measure(); - auto times2 = st_collector2.measure(); - auto times_scoped = st_collector_scoped.measure(); - - auto median1 = utils::median(times1.begin(), times1.end()); - auto median2 = utils::median(times2.begin(), times2.end()); - auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); - - WARN_MESSAGE(median1 < median2, - "Expected workers start new work faster when using parallel_phase"); - - WARN_MESSAGE(median_scoped < median2, - "Expected workers start new work faster when using scoped parallel_phase"); -} - -//! \brief \ref interface \ref requirement -TEST_CASE("Test one-time fast leave") { - tbb::task_arena ta1{}; - tbb::task_arena ta2{}; - start_time_collection st_collector1{ta1, /*num_trials=*/10}; - start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/10}; - start_time_collection_sequenced_scoped_phases st_collector_scoped{ta2, /*num_trials=*/10}; - - auto times1 = st_collector1.measure(); - auto times2 = st_collector2.measure(); - auto times_scoped = st_collector_scoped.measure(); - - auto median1 = utils::median(times1.begin(), times1.end()); - auto median2 = utils::median(times2.begin(), times2.end()); - auto median_scoped = utils::median(times_scoped.begin(), times_scoped.end()); - - WARN_MESSAGE(median1 < median2, - "Expected one-time fast leave setting to slow workers to start new work"); - - WARN_MESSAGE(median1 < median_scoped, - "Expected one-time fast leave setting to slow workers to start new work"); -} - -//! \brief \ref interface \ref requirement -TEST_CASE("Test parallel phase with this_task_arena") { - start_time_collection st_collector1{/*num_trials=*/10}; - start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10}; - - auto times1 = st_collector1.measure(); - auto times2 = st_collector2.measure(); - - auto median1 = utils::median(times1.begin(), times1.end()); - auto median2 = utils::median(times2.begin(), times2.end()); - - WARN_MESSAGE(median1 < median2, - "Expected one-time fast leave setting to slow workers to start new work"); -} - -#endif From 4dfaf5e7c3a5d56bcc37a1753203aa9102747d53 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 17 Dec 2024 17:52:58 +0100 Subject: [PATCH 25/35] Address comments Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 20 +++++------- src/tbb/arena.cpp | 16 +++++----- src/tbb/arena.h | 21 +++++++------ src/tbb/waiters.h | 6 ++-- test/tbb/test_parallel_phase.cpp | 53 ++++++++++++++++++++++---------- 5 files changed, 68 insertions(+), 48 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 3f0bc5a611..3eb007dcc8 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -118,11 +118,6 @@ namespace d1 { static constexpr unsigned num_priority_levels = 3; static constexpr int priority_stride = INT_MAX / (num_priority_levels + 1); -#if __TBB_PREVIEW_PARALLEL_PHASE -static constexpr int leave_policy_trait_offset = 1; -static constexpr int leave_policy_trait_mask = ~((1 << leave_policy_trait_offset) - 1); -#endif - class task_arena_base { friend struct r1::task_arena_impl; friend void r1::observe(d1::task_scheduler_observer&, bool); @@ -181,23 +176,24 @@ class task_arena_base { } #if __TBB_PREVIEW_PARALLEL_PHASE - int leave_policy_to_traits(leave_policy lp) const { - return static_cast(lp) << leave_policy_trait_offset; + leave_policy get_leave_policy() const { + bool fast_policy_set = (my_version_and_traits & fast_leave_policy_flag) == fast_leave_policy_flag; + return fast_policy_set ? leave_policy::fast : leave_policy::automatic; } - leave_policy get_leave_policy() const { - int underlying_policy_v = (my_version_and_traits & leave_policy_trait_mask) >> leave_policy_trait_offset; - return static_cast(underlying_policy_v); + int leave_policy_to_traits(leave_policy lp) const { + return lp == leave_policy::fast ? fast_leave_policy_flag : 0; } void set_leave_policy(leave_policy lp) { - my_version_and_traits = (my_version_and_traits & ~leave_policy_trait_mask) | leave_policy_to_traits(lp); + my_version_and_traits |= leave_policy_to_traits(lp); } #endif enum { default_flags = 0, - core_type_support_flag = 1 + core_type_support_flag = 1, + fast_leave_policy_flag = 1 << 1 }; task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 1f37ba9bf5..0791869bf5 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -530,8 +530,8 @@ struct task_arena_impl { static int max_concurrency(const d1::task_arena_base*); static void enqueue(d1::task&, d1::task_group_context*, d1::task_arena_base*); static d1::slot_id execution_slot(const d1::task_arena_base&); - static void register_parallel_phase(d1::task_arena_base*); - static void unregister_parallel_phase(d1::task_arena_base*, bool); + static void register_parallel_phase(d1::task_arena_base*, std::uintptr_t); + static void unregister_parallel_phase(d1::task_arena_base*, std::uintptr_t); }; void __TBB_EXPORTED_FUNC initialize(d1::task_arena_base& ta) { @@ -566,12 +566,12 @@ d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::task_arena_base& arena) return task_arena_impl::execution_slot(arena); } -void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base* ta, std::uintptr_t /*reserved*/) { - task_arena_impl::register_parallel_phase(ta); +void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { + task_arena_impl::register_parallel_phase(ta, flags); } void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { - task_arena_impl::unregister_parallel_phase(ta, static_cast(flags)); + task_arena_impl::unregister_parallel_phase(ta, flags); } void task_arena_impl::initialize(d1::task_arena_base& ta) { @@ -921,17 +921,17 @@ int task_arena_impl::max_concurrency(const d1::task_arena_base *ta) { } #if __TBB_PREVIEW_PARALLEL_PHASE -void task_arena_impl::register_parallel_phase(d1::task_arena_base* ta) { +void task_arena_impl::register_parallel_phase(d1::task_arena_base* ta, std::uintptr_t /*reserved*/) { arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; __TBB_ASSERT(a, nullptr); a->my_thread_leave.register_parallel_phase(); a->advertise_new_work(); } -void task_arena_impl::unregister_parallel_phase(d1::task_arena_base* ta, bool with_fast_leave) { +void task_arena_impl::unregister_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; __TBB_ASSERT(a, nullptr); - a->my_thread_leave.unregister_parallel_phase(with_fast_leave); + a->my_thread_leave.unregister_parallel_phase(/*with_fast_leave=*/static_cast(flags)); } #endif diff --git a/src/tbb/arena.h b/src/tbb/arena.h index fcc6795109..72cfc7feda 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -210,9 +210,9 @@ class thread_leave_manager { // Indicate start of parallel phase in the state machine void register_parallel_phase() { - __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uint64_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != 0, "The initial state was not set"); + std::uint64_t desired{}; do { // Need to add a reference for this start of a parallel phase, preserving the leave @@ -224,17 +224,20 @@ class thread_leave_manager { // of new parallel phase, it should be transitioned to "Delayed leave" desired = DELAYED_LEAVE; } + __TBB_ASSERT(desired + PARALLEL_PHASE > desired, "Overflow detected"); desired += PARALLEL_PHASE; // Take into account this start of a parallel phase } while (!my_state.compare_exchange_strong(prev, desired)); } // Indicate the end of parallel phase in the state machine void unregister_parallel_phase(bool enable_fast_leave) { - __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uint64_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != 0, "The initial state was not set"); + std::uint64_t desired{}; do { + __TBB_ASSERT(prev - PARALLEL_PHASE < prev, + "A call to unregister without its register complement"); desired = prev - PARALLEL_PHASE; // Mark the end of this phase in reference counter if (enable_fast_leave && /*it was the last parallel phase*/desired == DELAYED_LEAVE) { desired = ONE_TIME_FAST_LEAVE; @@ -243,9 +246,9 @@ class thread_leave_manager { } bool is_retention_allowed() { - __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != 0, "The initial state was not set"); - std::uint64_t curr = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(curr != 0, "The initial state was not set"); + return curr != FAST_LEAVE && curr != ONE_TIME_FAST_LEAVE; } }; @@ -360,7 +363,7 @@ class arena: public padded //! Constructor arena(threading_control* control, unsigned max_num_workers, unsigned num_reserved_slots, unsigned priority_level #if __TBB_PREVIEW_PARALLEL_PHASE - , tbb::task_arena::leave_policy wl + , tbb::task_arena::leave_policy lp #endif ); @@ -368,7 +371,7 @@ class arena: public padded static arena& allocate_arena(threading_control* control, unsigned num_slots, unsigned num_reserved_slots, unsigned priority_level #if __TBB_PREVIEW_PARALLEL_PHASE - , tbb::task_arena::leave_policy wl + , tbb::task_arena::leave_policy lp #endif ); @@ -376,7 +379,7 @@ class arena: public padded unsigned arena_priority_level, d1::constraints constraints = d1::constraints{} #if __TBB_PREVIEW_PARALLEL_PHASE - , tbb::task_arena::leave_policy wl = tbb::task_arena::leave_policy::automatic + , tbb::task_arena::leave_policy lp = tbb::task_arena::leave_policy::automatic #endif ); diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index a217933b7d..e3248bb77f 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -61,7 +61,7 @@ class outermost_worker_waiter : public waiter_base { if (is_delayed_leave_enabled()) { static constexpr std::chrono::microseconds worker_wait_leave_duration(1000); static_assert(worker_wait_leave_duration > std::chrono::steady_clock::duration(1), - "Clock resolution is not enough for measured interval."); + "Clock resolution is not enough for measured interval."); for (auto t1 = std::chrono::steady_clock::now(), t2 = t1; std::chrono::duration_cast(t2 - t1) < worker_wait_leave_duration; @@ -71,7 +71,9 @@ class outermost_worker_waiter : public waiter_base { return true; } - if (!my_arena.my_thread_leave.is_retention_allowed() || my_arena.my_threading_control->is_any_other_client_active()) { + if (!my_arena.my_thread_leave.is_retention_allowed() || + my_arena.my_threading_control->is_any_other_client_active()) + { break; } d0::yield(); diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index df6119bfed..8b5f9404fa 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -12,7 +12,7 @@ struct dummy_func { } }; -template +template std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{}, const F2& end = F2{}) { std::size_t num_threads = ta ? ta->max_concurrency() : tbb::this_task_arena::max_concurrency(); std::size_t num_runs = 1000; @@ -120,42 +120,62 @@ class start_time_collection_sequenced_phases : public start_time_collection_base { using base = start_time_collection_base; - using base::base; friend base; + bool with_fast_leave; + std::size_t measure_impl() { std::size_t median_start_time; if (arena) { median_start_time = measure_median_start_time(arena, [this] { arena->start_parallel_phase(); }, - [this] { arena->end_parallel_phase(/*with_fast_leave=*/true); }); + [this] { arena->end_parallel_phase(with_fast_leave); }); } else { median_start_time = measure_median_start_time(arena, [] { tbb::this_task_arena::start_parallel_phase(); }, - [] { tbb::this_task_arena::end_parallel_phase(/*with_fast_leave=*/true); }); + [this] { tbb::this_task_arena::end_parallel_phase(with_fast_leave); }); } return median_start_time; }; + +public: + start_time_collection_sequenced_phases(tbb::task_arena& ta, std::size_t ntrials, bool fast_leave = false) : + base(ta, ntrials), with_fast_leave(fast_leave) + {} + + explicit start_time_collection_sequenced_phases(std::size_t ntrials, bool fast_leave = false) : + base(ntrials), with_fast_leave(fast_leave) + {} }; class start_time_collection_sequenced_scoped_phases : public start_time_collection_base { using base = start_time_collection_base; - using base::base; friend base; + bool with_fast_leave; + std::size_t measure_impl() { tbb::task_arena::scoped_parallel_phase* phase = nullptr; auto median_start_time = measure_median_start_time(arena, [this, &phase] { - phase = new tbb::task_arena::scoped_parallel_phase{*arena, /*with_fast_leave=*/true}; + phase = new tbb::task_arena::scoped_parallel_phase{*arena, with_fast_leave}; }, [&phase] { delete phase; }); return median_start_time; }; + +public: + start_time_collection_sequenced_scoped_phases(tbb::task_arena& ta, std::size_t ntrials, bool fast_leave = false) : + base(ta, ntrials), with_fast_leave(fast_leave) + {} + + explicit start_time_collection_sequenced_scoped_phases(std::size_t ntrials, bool fast_leave = false) : + base(ntrials), with_fast_leave(fast_leave) + {} }; //! \brief \ref interface \ref requirement @@ -170,8 +190,8 @@ TEST_CASE("Check that workers leave faster with leave_policy::fast") { tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast }; - start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/20}; - start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/20}; + start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/10}; + start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/10}; auto times_automatic = st_collector1.measure(); auto times_fast = st_collector2.measure(); @@ -195,9 +215,9 @@ TEST_CASE("Parallel Phase retains workers in task_arena") { tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast }; - start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/20}; - start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/20}; - start_time_collection st_collector2{ta_fast2, /*num_trials=*/20}; + start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/10}; + start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/10}; + start_time_collection st_collector2{ta_fast2, /*num_trials=*/10}; auto times1 = st_collector1.measure(); auto times2 = st_collector2.measure(); @@ -218,9 +238,9 @@ TEST_CASE("Parallel Phase retains workers in task_arena") { TEST_CASE("Test one-time fast leave") { tbb::task_arena ta1{}; tbb::task_arena ta2{}; - start_time_collection st_collector1{ta1, /*num_trials=*/10}; - start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/10}; - start_time_collection_sequenced_scoped_phases st_collector_scoped{ta2, /*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector1{ta1, /*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{ta2, /*num_trials=*/10, /*fast_leave*/true}; + start_time_collection_sequenced_scoped_phases st_collector_scoped{ta2, /*num_trials=*/10, /*fast_leave*/true}; auto times1 = st_collector1.measure(); auto times2 = st_collector2.measure(); @@ -239,8 +259,8 @@ TEST_CASE("Test one-time fast leave") { //! \brief \ref interface \ref requirement TEST_CASE("Test parallel phase with this_task_arena") { - start_time_collection st_collector1{/*num_trials=*/10}; - start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector1{/*num_trials=*/10}; + start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10, /*fast_leave*/true}; auto times1 = st_collector1.measure(); auto times2 = st_collector2.measure(); @@ -251,4 +271,3 @@ TEST_CASE("Test parallel phase with this_task_arena") { WARN_MESSAGE(median1 < median2, "Expected one-time fast leave setting to slow workers to start new work"); } - From 6a951bdad074cf236a94d4f1ed76e82e4be9f660 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Fri, 20 Dec 2024 16:14:06 +0100 Subject: [PATCH 26/35] Decrease the size of dummy work, don't execute tests for workerless arena Signed-off-by: Isaev, Ilya --- test/tbb/test_parallel_phase.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index 8b5f9404fa..47350ce101 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -2,6 +2,7 @@ #include "common/test.h" #include "common/utils.h" +#include "common/utils_concurrency_limit.h" #include "common/spin_barrier.h" #include "tbb/task_arena.h" @@ -49,7 +50,7 @@ std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{ } else { work(); } - utils::doDummyWork(i*500); + utils::doDummyWork(i*250); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -180,6 +181,11 @@ class start_time_collection_sequenced_scoped_phases //! \brief \ref interface \ref requirement TEST_CASE("Check that workers leave faster with leave_policy::fast") { + // Test measures workers start time, so no there is no point to + // measure it with workerless arena + if (utils::get_platform_max_threads() < 2) { + return; + } tbb::task_arena ta_automatic_leave { tbb::task_arena::automatic, 1, tbb::task_arena::priority::normal, @@ -200,11 +206,14 @@ TEST_CASE("Check that workers leave faster with leave_policy::fast") { auto median_fast = utils::median(times_fast.begin(), times_fast.end()); WARN_MESSAGE(median_automatic < median_fast, - "Expected workers to start new work faster with delayed leave"); + "Expected workers to start new work slower with fast leave policy"); } //! \brief \ref interface \ref requirement TEST_CASE("Parallel Phase retains workers in task_arena") { + if (utils::get_platform_max_threads() < 2) { + return; + } tbb::task_arena ta_fast1 { tbb::task_arena::automatic, 1, tbb::task_arena::priority::normal, @@ -236,6 +245,9 @@ TEST_CASE("Parallel Phase retains workers in task_arena") { //! \brief \ref interface \ref requirement TEST_CASE("Test one-time fast leave") { + if (utils::get_platform_max_threads() < 2) { + return; + } tbb::task_arena ta1{}; tbb::task_arena ta2{}; start_time_collection_sequenced_phases st_collector1{ta1, /*num_trials=*/10}; @@ -259,6 +271,9 @@ TEST_CASE("Test one-time fast leave") { //! \brief \ref interface \ref requirement TEST_CASE("Test parallel phase with this_task_arena") { + if (utils::get_platform_max_threads() < 2) { + return; + } start_time_collection_sequenced_phases st_collector1{/*num_trials=*/10}; start_time_collection_sequenced_phases st_collector2{/*num_trials=*/10, /*fast_leave*/true}; From 9d507cac9ff4dd5357328cdacdd1a16a3e324a82 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Tue, 14 Jan 2025 12:29:58 +0100 Subject: [PATCH 27/35] Apply suggestions from code review Co-authored-by: Alexey Kukanov Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/detail/_config.h | 2 +- include/oneapi/tbb/task_arena.h | 15 +++++------ src/tbb/arena.cpp | 4 +-- src/tbb/arena.h | 41 +++++++++++++++-------------- src/tbb/def/lin32-tbb.def | 2 +- src/tbb/def/lin64-tbb.def | 2 +- src/tbb/def/mac64-tbb.def | 2 +- src/tbb/def/win32-tbb.def | 2 +- src/tbb/def/win64-tbb.def | 2 +- src/tbb/waiters.h | 2 +- test/CMakeLists.txt | 2 +- test/tbb/test_parallel_phase.cpp | 16 +++++++++++ 12 files changed, 54 insertions(+), 38 deletions(-) diff --git a/include/oneapi/tbb/detail/_config.h b/include/oneapi/tbb/detail/_config.h index bd7c50e7b4..87a33e20ff 100644 --- a/include/oneapi/tbb/detail/_config.h +++ b/include/oneapi/tbb/detail/_config.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 3eb007dcc8..54b9220021 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -177,16 +177,15 @@ class task_arena_base { #if __TBB_PREVIEW_PARALLEL_PHASE leave_policy get_leave_policy() const { - bool fast_policy_set = (my_version_and_traits & fast_leave_policy_flag) == fast_leave_policy_flag; - return fast_policy_set ? leave_policy::fast : leave_policy::automatic; + return (my_version_and_traits & fast_leave_policy_flag) ? leave_policy::fast : leave_policy::automatic; } - int leave_policy_to_traits(leave_policy lp) const { + int leave_policy_trait(leave_policy lp) const { return lp == leave_policy::fast ? fast_leave_policy_flag : 0; } void set_leave_policy(leave_policy lp) { - my_version_and_traits |= leave_policy_to_traits(lp); + my_version_and_traits |= leave_policy_trait(lp); } #endif @@ -203,7 +202,7 @@ class task_arena_base { ) : my_version_and_traits(default_flags | core_type_support_flag #if __TBB_PREVIEW_PARALLEL_PHASE - | leave_policy_to_traits(lp) + | leave_policy_trait(lp) #endif ) , my_initialization_state(do_once_state::uninitialized) @@ -224,7 +223,7 @@ class task_arena_base { ) : my_version_and_traits(default_flags | core_type_support_flag #if __TBB_PREVIEW_PARALLEL_PHASE - | leave_policy_to_traits(lp) + | leave_policy_trait(lp) #endif ) , my_initialization_state(do_once_state::uninitialized) @@ -306,7 +305,7 @@ class task_arena : public task_arena_base { task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, priority a_priority = priority::normal #if __TBB_PREVIEW_PARALLEL_PHASE - , leave_policy lp = leave_policy::automatic + , leave_policy lp = leave_policy::automatic #endif ) : task_arena_base(max_concurrency_, reserved_for_masters, a_priority diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 0791869bf5..79080884f7 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -358,7 +358,7 @@ bool arena::has_enqueued_tasks() { void arena::request_workers(int mandatory_delta, int workers_delta, bool wakeup_threads) { #if __TBB_PREVIEW_PARALLEL_PHASE - my_thread_leave.restore_default_policy_if_needed(); + my_thread_leave.reset_if_needed(); #endif my_threading_control->adjust_demand(my_tc_client, mandatory_delta, workers_delta); diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 72cfc7feda..fb02c1ab57 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -1,5 +1,5 @@ -/* - Copyright (c) 2005-2024 Intel Corporation +/*arena + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -181,16 +181,18 @@ class atomic_flag { #if __TBB_PREVIEW_PARALLEL_PHASE class thread_leave_manager { - static const std::uint64_t FAST_LEAVE = 1; - static const std::uint64_t ONE_TIME_FAST_LEAVE = 1 << 1; - static const std::uint64_t DELAYED_LEAVE = 1 << 2; - static const std::uint64_t PARALLEL_PHASE = 1 << 3; + static const std::size_t DELAYED_LEAVE = 0; + static const std::size_t FAST_LEAVE = 1; + static const std::size_t ONE_TIME_FAST_LEAVE = 1 << 1; + static const std::size_t PARALLEL_PHASE = 1 << 2; - std::atomic my_state{0}; + std::atomic my_state{static_cast(-1)}; public: + // This method is not thread-safe! + // Required to be called after construction to set initial state of the state machine. void set_initial_state(tbb::task_arena::leave_policy lp) { if (lp == tbb::task_arena::leave_policy::automatic) { - std::uint64_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; + std::size_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; my_state.store(platform_policy, std::memory_order_relaxed); } else { __TBB_ASSERT(lp == tbb::task_arena::leave_policy::fast, @@ -199,21 +201,21 @@ class thread_leave_manager { } } - void restore_default_policy_if_needed() { - std::uint64_t curr = ONE_TIME_FAST_LEAVE; + void reset_if_needed() { + std::size_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { // Potentially can override decision of the parallel block from future epoch // but it is not a problem because it does not violate the correctness my_state.compare_exchange_strong(curr, DELAYED_LEAVE); } } - + // Indicate start of parallel phase in the state machine void register_parallel_phase() { - std::uint64_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != 0, "The initial state was not set"); + std::size_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != std::size_t(-1), "The initial state was not set"); - std::uint64_t desired{}; + std::size_t desired{}; do { // Need to add a reference for this start of a parallel phase, preserving the leave // policy. Except for the case when one time fast leave was requested at the end of a @@ -231,10 +233,10 @@ class thread_leave_manager { // Indicate the end of parallel phase in the state machine void unregister_parallel_phase(bool enable_fast_leave) { - std::uint64_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != 0, "The initial state was not set"); + std::size_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != std::size_t(-1), "The initial state was not set"); - std::uint64_t desired{}; + std::size_t desired{}; do { __TBB_ASSERT(prev - PARALLEL_PHASE < prev, "A call to unregister without its register complement"); @@ -246,9 +248,8 @@ class thread_leave_manager { } bool is_retention_allowed() { - std::uint64_t curr = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(curr != 0, "The initial state was not set"); - + std::size_t curr = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(curr != std::size_t(-1), "The initial state was not set"); return curr != FAST_LEAVE && curr != ONE_TIME_FAST_LEAVE; } }; diff --git a/src/tbb/def/lin32-tbb.def b/src/tbb/def/lin32-tbb.def index b71e08e5bb..3cc322f2a3 100644 --- a/src/tbb/def/lin32-tbb.def +++ b/src/tbb/def/lin32-tbb.def @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/tbb/def/lin64-tbb.def b/src/tbb/def/lin64-tbb.def index 71be72b678..9c0cdc1022 100644 --- a/src/tbb/def/lin64-tbb.def +++ b/src/tbb/def/lin64-tbb.def @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/tbb/def/mac64-tbb.def b/src/tbb/def/mac64-tbb.def index 1895dbdbbb..fe1daed52c 100644 --- a/src/tbb/def/mac64-tbb.def +++ b/src/tbb/def/mac64-tbb.def @@ -1,4 +1,4 @@ -# Copyright (c) 2005-2024 Intel Corporation +# Copyright (c) 2005-2025 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/tbb/def/win32-tbb.def b/src/tbb/def/win32-tbb.def index 4813495fa5..1ce826c120 100644 --- a/src/tbb/def/win32-tbb.def +++ b/src/tbb/def/win32-tbb.def @@ -1,4 +1,4 @@ -; Copyright (c) 2005-2024 Intel Corporation +; Copyright (c) 2005-2025 Intel Corporation ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. diff --git a/src/tbb/def/win64-tbb.def b/src/tbb/def/win64-tbb.def index 5f417b49da..e80029e976 100644 --- a/src/tbb/def/win64-tbb.def +++ b/src/tbb/def/win64-tbb.def @@ -1,4 +1,4 @@ -; Copyright (c) 2005-2024 Intel Corporation +; Copyright (c) 2005-2025 Intel Corporation ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. diff --git a/src/tbb/waiters.h b/src/tbb/waiters.h index e3248bb77f..5ec2710f31 100644 --- a/src/tbb/waiters.h +++ b/src/tbb/waiters.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d308dc2367..04aa8b5001 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2024 Intel Corporation +# Copyright (c) 2020-2025 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index 47350ce101..cabc128a77 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -1,3 +1,19 @@ +/* + Copyright (c) 2025 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + #define TBB_PREVIEW_PARALLEL_PHASE 1 #include "common/test.h" From 515fd3341faac621f788ea3d3c293b7ba5648848 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 15 Jan 2025 15:29:52 +0100 Subject: [PATCH 28/35] Improve test stability Signed-off-by: Isaev, Ilya --- test/tbb/test_parallel_phase.cpp | 98 ++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 31 deletions(-) diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index cabc128a77..38cc528724 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -16,6 +16,8 @@ #define TBB_PREVIEW_PARALLEL_PHASE 1 +#include + #include "common/test.h" #include "common/utils.h" #include "common/utils_concurrency_limit.h" @@ -24,6 +26,15 @@ #include "tbb/task_arena.h" #include "tbb/parallel_for.h" +void active_wait_for(std::chrono::microseconds duration) { + for (auto t1 = std::chrono::steady_clock::now(), t2 = t1; + std::chrono::duration_cast(t2 - t1) < duration; + t2 = std::chrono::steady_clock::now()) + { + utils::doDummyWork(100); + } +} + struct dummy_func { void operator()() const { } @@ -66,7 +77,7 @@ std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{ } else { work(); } - utils::doDummyWork(i*250); + active_wait_for(std::chrono::microseconds(i)); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -101,7 +112,7 @@ class start_time_collection : public start_time_collection_baseend_parallel_phase(/*with_fast_leave*/true); return median_start_time; - }; + } }; class start_time_collection_scoped_phase_wrapped @@ -130,7 +141,7 @@ class start_time_collection_scoped_phase_wrapped tbb::task_arena::scoped_parallel_phase phase{*arena}; auto median_start_time = measure_median_start_time(arena); return median_start_time; - }; + } }; class start_time_collection_sequenced_phases @@ -143,17 +154,35 @@ class start_time_collection_sequenced_phases std::size_t measure_impl() { std::size_t median_start_time; + utils::SpinBarrier barrier; + auto body = [&] (std::size_t) { + barrier.wait(); + }; if (arena) { + barrier.initialize(arena->max_concurrency()); median_start_time = measure_median_start_time(arena, - [this] { arena->start_parallel_phase(); }, - [this] { arena->end_parallel_phase(with_fast_leave); }); + [&] { + std::size_t num_threads = arena->max_concurrency(); + arena->start_parallel_phase(); + arena->execute([&] { + tbb::parallel_for(std::size_t(0), num_threads, body, tbb::static_partitioner{}); + }); + arena->end_parallel_phase(with_fast_leave); + } + ); } else { + barrier.initialize(tbb::this_task_arena::max_concurrency()); median_start_time = measure_median_start_time(arena, - [] { tbb::this_task_arena::start_parallel_phase(); }, - [this] { tbb::this_task_arena::end_parallel_phase(with_fast_leave); }); + [&] { + std::size_t num_threads = tbb::this_task_arena::max_concurrency(); + tbb::this_task_arena::start_parallel_phase(); + tbb::parallel_for(std::size_t(0), num_threads, body, tbb::static_partitioner{}); + tbb::this_task_arena::end_parallel_phase(with_fast_leave); + } + ); } return median_start_time; - }; + } public: start_time_collection_sequenced_phases(tbb::task_arena& ta, std::size_t ntrials, bool fast_leave = false) : @@ -168,31 +197,38 @@ class start_time_collection_sequenced_phases class start_time_collection_sequenced_scoped_phases : public start_time_collection_base { - using base = start_time_collection_base; - friend base; - - bool with_fast_leave; - - std::size_t measure_impl() { - tbb::task_arena::scoped_parallel_phase* phase = nullptr; - auto median_start_time = measure_median_start_time(arena, - [this, &phase] { - phase = new tbb::task_arena::scoped_parallel_phase{*arena, with_fast_leave}; - }, - [&phase] { - delete phase; - }); - return median_start_time; - }; + using base = start_time_collection_base; + friend base; + + bool with_fast_leave; + + std::size_t measure_impl() { + utils::SpinBarrier barrier{static_cast(arena->max_concurrency())}; + auto body = [&] (std::size_t) { + barrier.wait(); + }; + auto median_start_time = measure_median_start_time(arena, + [&] { + std::size_t num_threads = arena->max_concurrency(); + { + tbb::task_arena::scoped_parallel_phase phase{*arena, with_fast_leave}; + arena->execute([&] { + tbb::parallel_for(std::size_t(0), num_threads, body, tbb::static_partitioner{}); + }); + } + } + ); + return median_start_time; + } public: - start_time_collection_sequenced_scoped_phases(tbb::task_arena& ta, std::size_t ntrials, bool fast_leave = false) : - base(ta, ntrials), with_fast_leave(fast_leave) - {} + start_time_collection_sequenced_scoped_phases(tbb::task_arena& ta, std::size_t ntrials, bool fast_leave = false) : + base(ta, ntrials), with_fast_leave(fast_leave) + {} - explicit start_time_collection_sequenced_scoped_phases(std::size_t ntrials, bool fast_leave = false) : - base(ntrials), with_fast_leave(fast_leave) - {} + explicit start_time_collection_sequenced_scoped_phases(std::size_t ntrials, bool fast_leave = false) : + base(ntrials), with_fast_leave(fast_leave) + {} }; //! \brief \ref interface \ref requirement From 0f233bbb3826c844b1e47fc45d8471ecf44a5b8a Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 15 Jan 2025 16:07:26 +0100 Subject: [PATCH 29/35] Use uintptr_t Signed-off-by: Isaev, Ilya --- src/tbb/arena.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index fb02c1ab57..0df2a417d2 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -181,18 +181,18 @@ class atomic_flag { #if __TBB_PREVIEW_PARALLEL_PHASE class thread_leave_manager { - static const std::size_t DELAYED_LEAVE = 0; - static const std::size_t FAST_LEAVE = 1; - static const std::size_t ONE_TIME_FAST_LEAVE = 1 << 1; - static const std::size_t PARALLEL_PHASE = 1 << 2; + static const std::uintptr_t DELAYED_LEAVE = 0; + static const std::uintptr_t FAST_LEAVE = 1; + static const std::uintptr_t ONE_TIME_FAST_LEAVE = 1 << 1; + static const std::uintptr_t PARALLEL_PHASE = 1 << 2; - std::atomic my_state{static_cast(-1)}; + std::atomic my_state{static_cast(-1)}; public: // This method is not thread-safe! // Required to be called after construction to set initial state of the state machine. void set_initial_state(tbb::task_arena::leave_policy lp) { if (lp == tbb::task_arena::leave_policy::automatic) { - std::size_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; + std::uintptr_t platform_policy = governor::hybrid_cpu() ? FAST_LEAVE : DELAYED_LEAVE; my_state.store(platform_policy, std::memory_order_relaxed); } else { __TBB_ASSERT(lp == tbb::task_arena::leave_policy::fast, @@ -202,7 +202,7 @@ class thread_leave_manager { } void reset_if_needed() { - std::size_t curr = ONE_TIME_FAST_LEAVE; + std::uintptr_t curr = ONE_TIME_FAST_LEAVE; if (my_state.load(std::memory_order_relaxed) == curr) { // Potentially can override decision of the parallel block from future epoch // but it is not a problem because it does not violate the correctness @@ -212,10 +212,10 @@ class thread_leave_manager { // Indicate start of parallel phase in the state machine void register_parallel_phase() { - std::size_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != std::size_t(-1), "The initial state was not set"); + std::uintptr_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != std::uintptr_t(-1), "The initial state was not set"); - std::size_t desired{}; + std::uintptr_t desired{}; do { // Need to add a reference for this start of a parallel phase, preserving the leave // policy. Except for the case when one time fast leave was requested at the end of a @@ -233,10 +233,10 @@ class thread_leave_manager { // Indicate the end of parallel phase in the state machine void unregister_parallel_phase(bool enable_fast_leave) { - std::size_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != std::size_t(-1), "The initial state was not set"); + std::uintptr_t prev = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(prev != std::uintptr_t(-1), "The initial state was not set"); - std::size_t desired{}; + std::uintptr_t desired{}; do { __TBB_ASSERT(prev - PARALLEL_PHASE < prev, "A call to unregister without its register complement"); @@ -248,8 +248,8 @@ class thread_leave_manager { } bool is_retention_allowed() { - std::size_t curr = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(curr != std::size_t(-1), "The initial state was not set"); + std::uintptr_t curr = my_state.load(std::memory_order_relaxed); + __TBB_ASSERT(curr != std::uintptr_t(-1), "The initial state was not set"); return curr != FAST_LEAVE && curr != ONE_TIME_FAST_LEAVE; } }; From 15c3ea5af76d3383d5f0b8fe85406a847e8c889e Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 15 Jan 2025 16:20:17 +0100 Subject: [PATCH 30/35] Update copyright years Signed-off-by: Isaev, Ilya --- test/common/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/config.h b/test/common/config.h index 7ae67a6224..2330b2be8c 100644 --- a/test/common/config.h +++ b/test/common/config.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2024 Intel Corporation + Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From f041f3a49d97614c2965e8cb201c2138c75a452c Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 15 Jan 2025 18:38:14 +0100 Subject: [PATCH 31/35] Reduce testing time Signed-off-by: Isaev, Ilya --- test/tbb/test_parallel_phase.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index 38cc528724..180a87f527 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -43,7 +43,7 @@ struct dummy_func { template std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{}, const F2& end = F2{}) { std::size_t num_threads = ta ? ta->max_concurrency() : tbb::this_task_arena::max_concurrency(); - std::size_t num_runs = 1000; + std::size_t num_runs = 500; std::vector longest_start_times; longest_start_times.reserve(num_runs); @@ -71,13 +71,13 @@ std::size_t measure_median_start_time(tbb::task_arena* ta, const F1& start = F1{ longest_start_times.push_back(get_longest_start(start_time)); }; - for (std::size_t i = 0; i < num_runs; ++i) { + for (std::size_t i = 1; i < num_runs; ++i) { if (ta) { ta->execute(work); } else { work(); } - active_wait_for(std::chrono::microseconds(i)); + active_wait_for(std::chrono::microseconds(i*2)); } return utils::median(longest_start_times.begin(), longest_start_times.end()); } @@ -248,8 +248,8 @@ TEST_CASE("Check that workers leave faster with leave_policy::fast") { tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast }; - start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/10}; - start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/10}; + start_time_collection st_collector1{ta_automatic_leave, /*num_trials=*/5}; + start_time_collection st_collector2{ta_fast_leave, /*num_trials=*/5}; auto times_automatic = st_collector1.measure(); auto times_fast = st_collector2.measure(); @@ -276,9 +276,9 @@ TEST_CASE("Parallel Phase retains workers in task_arena") { tbb::task_arena::priority::normal, tbb::task_arena::leave_policy::fast }; - start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/10}; - start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/10}; - start_time_collection st_collector2{ta_fast2, /*num_trials=*/10}; + start_time_collection_phase_wrapped st_collector1{ta_fast1, /*num_trials=*/5}; + start_time_collection_scoped_phase_wrapped st_collector_scoped{ta_fast1, /*num_trials=*/5}; + start_time_collection st_collector2{ta_fast2, /*num_trials=*/5}; auto times1 = st_collector1.measure(); auto times2 = st_collector2.measure(); From fdae1510353eb723beb8a53dc50ce82fd3202869 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 15 Jan 2025 18:39:10 +0100 Subject: [PATCH 32/35] Move RFC to the experimental stage Signed-off-by: Isaev, Ilya --- .../parallel_phase_for_task_arena}/README.md | 34 +++++++++++++++++- .../alternative_proposal.png | Bin .../completely_disable_new_behavior.png | Bin .../parallel_phase_introduction.png | Bin .../parallel_phase_sequence_diagram.png | Bin 0 -> 126211 bytes .../parallel_phase_state_final.png | Bin .../parallel_phase_state_initial.png | Bin 7 files changed, 33 insertions(+), 1 deletion(-) rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/README.md (87%) rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/alternative_proposal.png (100%) rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/completely_disable_new_behavior.png (100%) rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/parallel_phase_introduction.png (100%) create mode 100644 rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_sequence_diagram.png rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/parallel_phase_state_final.png (100%) rename rfcs/{proposed/parallel_block_for_task_arena => experimental/parallel_phase_for_task_arena}/parallel_phase_state_initial.png (100%) diff --git a/rfcs/proposed/parallel_block_for_task_arena/README.md b/rfcs/experimental/parallel_phase_for_task_arena/README.md similarity index 87% rename from rfcs/proposed/parallel_block_for_task_arena/README.md rename to rfcs/experimental/parallel_phase_for_task_arena/README.md index 207c318b4d..ade16c2dbd 100644 --- a/rfcs/proposed/parallel_block_for_task_arena/README.md +++ b/rfcs/experimental/parallel_phase_for_task_arena/README.md @@ -235,7 +235,6 @@ void scoped_parallel_phase_example() { // Computation } } - ``` ## Considerations @@ -256,6 +255,32 @@ it might introduce performance problems if: Heavier involvement of less performant core types might result in artificial work imbalance in the arena. +## Technical Details + +To implement the proposed feature, the following changes were made: +* Added a new entity `thread_leave_manager` to the `r1::arena` which is responsible for + for managing the state of workers' arena leaving behaviour. +* Introduced two new entry points to the library. + * `r1::register_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate + the start of parallel phase with the library. + * `r1::unregister_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate + the end of parallel phase with the library. + +### Thread Leave Manager + +`thread_leave_manager` class implements the state machine described in proposal. +Specifically, it controls when worker threads are allowed to be retained in the arena. +`thread_leave_manager` is initialized with a state that determines the default +behavior for workers leaving the arena. + +To support `start/end_parallel_phase` API, it provides functionality to override the default +state with a "Parallel Phase" state. It also keeps track of the number of active parallel phases. + +The following sequence diagram illustrates the interaction between the user and +the `thread_leave_manager` during the execution of parallel phases. It shows how the +`thread_leave_manager` manages the state transitions when using `start/end_parallel_phase`. + + ## Open Questions in Design @@ -272,3 +297,10 @@ Some open questions that remain: * Do we see any value if arena potentially can transition from one to another state? * What if different types of workloads are mixed in one application? * What if there concurrent calls to this API? + +## Conditions to become fully supported + +Following conditions need to be met for the feature to move from experimental to fully supported: +* Open questions regarding API should be resolved. +* The feature should demonstrate performance improvements in scenarios mentioned. +* oneTBB specification needs to be updated to reflect the new feature. diff --git a/rfcs/proposed/parallel_block_for_task_arena/alternative_proposal.png b/rfcs/experimental/parallel_phase_for_task_arena/alternative_proposal.png similarity index 100% rename from rfcs/proposed/parallel_block_for_task_arena/alternative_proposal.png rename to rfcs/experimental/parallel_phase_for_task_arena/alternative_proposal.png diff --git a/rfcs/proposed/parallel_block_for_task_arena/completely_disable_new_behavior.png b/rfcs/experimental/parallel_phase_for_task_arena/completely_disable_new_behavior.png similarity index 100% rename from rfcs/proposed/parallel_block_for_task_arena/completely_disable_new_behavior.png rename to rfcs/experimental/parallel_phase_for_task_arena/completely_disable_new_behavior.png diff --git a/rfcs/proposed/parallel_block_for_task_arena/parallel_phase_introduction.png b/rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_introduction.png similarity index 100% rename from rfcs/proposed/parallel_block_for_task_arena/parallel_phase_introduction.png rename to rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_introduction.png diff --git a/rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_sequence_diagram.png b/rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_sequence_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3933565e8d7879737e583748a3d30ca27fac74b3 GIT binary patch literal 126211 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 Date: Mon, 20 Jan 2025 14:28:48 +0100 Subject: [PATCH 33/35] Fix one time fast leave Signed-off-by: Isaev, Ilya --- src/tbb/arena.cpp | 3 --- src/tbb/arena.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tbb/arena.cpp b/src/tbb/arena.cpp index 79080884f7..8e58f7ad0a 100644 --- a/src/tbb/arena.cpp +++ b/src/tbb/arena.cpp @@ -357,9 +357,6 @@ bool arena::has_enqueued_tasks() { } void arena::request_workers(int mandatory_delta, int workers_delta, bool wakeup_threads) { -#if __TBB_PREVIEW_PARALLEL_PHASE - my_thread_leave.reset_if_needed(); -#endif my_threading_control->adjust_demand(my_tc_client, mandatory_delta, workers_delta); if (wakeup_threads) { diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 0df2a417d2..35f0ada9c7 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -528,6 +528,9 @@ void arena::advertise_new_work() { workers_delta = 1; } +#if __TBB_PREVIEW_PARALLEL_PHASE + my_thread_leave.reset_if_needed(); +#endif request_workers(mandatory_delta, workers_delta, /* wakeup_threads = */ true); } } From 0f1b1a53dd098d1d905e9fca256dd10f6a2e3bb5 Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Mon, 20 Jan 2025 17:41:47 +0100 Subject: [PATCH 34/35] Fix sources format Signed-off-by: Isaev, Ilya --- src/tbb/arena.h | 2 +- test/tbb/test_parallel_phase.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 35f0ada9c7..94b75489d5 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -1,4 +1,4 @@ -/*arena +/* Copyright (c) 2005-2025 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/tbb/test_parallel_phase.cpp b/test/tbb/test_parallel_phase.cpp index 180a87f527..b8ba2be012 100644 --- a/test/tbb/test_parallel_phase.cpp +++ b/test/tbb/test_parallel_phase.cpp @@ -14,6 +14,9 @@ limitations under the License. */ +//! \file test_parallel_phase.cpp +//! \brief Test for [preview] functionality + #define TBB_PREVIEW_PARALLEL_PHASE 1 #include From c38eb2ff64da04f9df1cbb3e4f6e25f573337cee Mon Sep 17 00:00:00 2001 From: "Isaev, Ilya" Date: Wed, 22 Jan 2025 12:23:12 +0100 Subject: [PATCH 35/35] Rename entry points, simplify state machine interaction Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/task_arena.h | 12 +++--- .../parallel_phase_for_task_arena/README.md | 4 +- .../parallel_phase_sequence_diagram.png | Bin 126211 -> 124339 bytes src/tbb/arena.cpp | 16 +++---- src/tbb/arena.h | 40 +++++++----------- src/tbb/def/lin32-tbb.def | 4 +- src/tbb/def/lin64-tbb.def | 4 +- src/tbb/def/mac64-tbb.def | 4 +- src/tbb/def/win32-tbb.def | 4 +- src/tbb/def/win64-tbb.def | 4 +- 10 files changed, 42 insertions(+), 50 deletions(-) diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 54b9220021..e594b57171 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -97,8 +97,8 @@ TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_group_context&, TBB_EXPORT void __TBB_EXPORTED_FUNC submit(d1::task&, d1::task_group_context&, arena*, std::uintptr_t); #if __TBB_PREVIEW_PARALLEL_PHASE -TBB_EXPORT void __TBB_EXPORTED_FUNC register_parallel_phase(d1::task_arena_base*, std::uintptr_t); -TBB_EXPORT void __TBB_EXPORTED_FUNC unregister_parallel_phase(d1::task_arena_base*, std::uintptr_t); +TBB_EXPORT void __TBB_EXPORTED_FUNC enter_parallel_phase(d1::task_arena_base*, std::uintptr_t); +TBB_EXPORT void __TBB_EXPORTED_FUNC exit_parallel_phase(d1::task_arena_base*, std::uintptr_t); #endif } // namespace r1 @@ -496,12 +496,12 @@ class task_arena : public task_arena_base { #if __TBB_PREVIEW_PARALLEL_PHASE void start_parallel_phase() { initialize(); - r1::register_parallel_phase(this, /*reserved*/0); + r1::enter_parallel_phase(this, /*reserved*/0); } void end_parallel_phase(bool with_fast_leave = false) { __TBB_ASSERT(my_initialization_state.load(std::memory_order_relaxed) == do_once_state::initialized, nullptr); // It is guaranteed by the standard that conversion of boolean to integral type will result in either 0 or 1 - r1::unregister_parallel_phase(this, static_cast(with_fast_leave)); + r1::exit_parallel_phase(this, static_cast(with_fast_leave)); } class scoped_parallel_phase { @@ -589,12 +589,12 @@ inline void enqueue(F&& f) { #if __TBB_PREVIEW_PARALLEL_PHASE inline void start_parallel_phase() { - r1::register_parallel_phase(nullptr, /*reserved*/0); + r1::enter_parallel_phase(nullptr, /*reserved*/0); } inline void end_parallel_phase(bool with_fast_leave) { // It is guaranteed by the standard that conversion of boolean to integral type will result in either 0 or 1 - r1::unregister_parallel_phase(nullptr, static_cast(with_fast_leave)); + r1::exit_parallel_phase(nullptr, static_cast(with_fast_leave)); } #endif diff --git a/rfcs/experimental/parallel_phase_for_task_arena/README.md b/rfcs/experimental/parallel_phase_for_task_arena/README.md index ade16c2dbd..34f3cf877d 100644 --- a/rfcs/experimental/parallel_phase_for_task_arena/README.md +++ b/rfcs/experimental/parallel_phase_for_task_arena/README.md @@ -261,9 +261,9 @@ To implement the proposed feature, the following changes were made: * Added a new entity `thread_leave_manager` to the `r1::arena` which is responsible for for managing the state of workers' arena leaving behaviour. * Introduced two new entry points to the library. - * `r1::register_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate + * `r1::enter_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate the start of parallel phase with the library. - * `r1::unregister_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate + * `r1::exit_parallel_phase(d1::task_arena_base*, std::uintptr_t)` - used to communicate the end of parallel phase with the library. ### Thread Leave Manager diff --git a/rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_sequence_diagram.png b/rfcs/experimental/parallel_phase_for_task_arena/parallel_phase_sequence_diagram.png index 3933565e8d7879737e583748a3d30ca27fac74b3..24648722efc8b901befbcbaa1e0be26181b93b73 100644 GIT binary patch literal 124339 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

ZgN0TLb#bo$F8PH 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 literal 126211 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.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; __TBB_ASSERT(a, nullptr); a->my_thread_leave.register_parallel_phase(); a->advertise_new_work(); } -void task_arena_impl::unregister_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { +void task_arena_impl::exit_parallel_phase(d1::task_arena_base* ta, std::uintptr_t flags) { arena* a = ta ? ta->my_arena.load(std::memory_order_relaxed) : governor::get_thread_data()->my_arena; __TBB_ASSERT(a, nullptr); a->my_thread_leave.unregister_parallel_phase(/*with_fast_leave=*/static_cast(flags)); diff --git a/src/tbb/arena.h b/src/tbb/arena.h index 94b75489d5..71aba8fa86 100644 --- a/src/tbb/arena.h +++ b/src/tbb/arena.h @@ -186,7 +186,7 @@ class thread_leave_manager { static const std::uintptr_t ONE_TIME_FAST_LEAVE = 1 << 1; static const std::uintptr_t PARALLEL_PHASE = 1 << 2; - std::atomic my_state{static_cast(-1)}; + std::atomic my_state{UINTPTR_MAX}; public: // This method is not thread-safe! // Required to be called after construction to set initial state of the state machine. @@ -202,39 +202,31 @@ class thread_leave_manager { } void reset_if_needed() { - std::uintptr_t curr = ONE_TIME_FAST_LEAVE; - if (my_state.load(std::memory_order_relaxed) == curr) { - // Potentially can override decision of the parallel block from future epoch + std::uintptr_t curr = my_state.load(std::memory_order_relaxed); + if (curr == ONE_TIME_FAST_LEAVE) { + // Potentially can override decision of the parallel phase from future epoch // but it is not a problem because it does not violate the correctness - my_state.compare_exchange_strong(curr, DELAYED_LEAVE); + my_state.fetch_and(~ONE_TIME_FAST_LEAVE); } } // Indicate start of parallel phase in the state machine void register_parallel_phase() { - std::uintptr_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != std::uintptr_t(-1), "The initial state was not set"); - - std::uintptr_t desired{}; - do { - // Need to add a reference for this start of a parallel phase, preserving the leave - // policy. Except for the case when one time fast leave was requested at the end of a - // previous phase. - desired = prev; - if (prev == ONE_TIME_FAST_LEAVE) { - // State was previously transitioned to "One-time Fast leave", thus with the start - // of new parallel phase, it should be transitioned to "Delayed leave" - desired = DELAYED_LEAVE; - } - __TBB_ASSERT(desired + PARALLEL_PHASE > desired, "Overflow detected"); - desired += PARALLEL_PHASE; // Take into account this start of a parallel phase - } while (!my_state.compare_exchange_strong(prev, desired)); + __TBB_ASSERT(my_state.load(std::memory_order_relaxed) != UINTPTR_MAX, "The initial state was not set"); + + std::uintptr_t prev = my_state.fetch_add(PARALLEL_PHASE); + __TBB_ASSERT(prev + PARALLEL_PHASE > prev, "Overflow detected"); + if (prev & ONE_TIME_FAST_LEAVE) { + // State was previously transitioned to "One-time Fast leave", thus with the start + // of new parallel phase, it should be reset + my_state.fetch_and(~ONE_TIME_FAST_LEAVE); + } } // Indicate the end of parallel phase in the state machine void unregister_parallel_phase(bool enable_fast_leave) { std::uintptr_t prev = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(prev != std::uintptr_t(-1), "The initial state was not set"); + __TBB_ASSERT(prev != UINTPTR_MAX, "The initial state was not set"); std::uintptr_t desired{}; do { @@ -249,7 +241,7 @@ class thread_leave_manager { bool is_retention_allowed() { std::uintptr_t curr = my_state.load(std::memory_order_relaxed); - __TBB_ASSERT(curr != std::uintptr_t(-1), "The initial state was not set"); + __TBB_ASSERT(curr != UINTPTR_MAX, "The initial state was not set"); return curr != FAST_LEAVE && curr != ONE_TIME_FAST_LEAVE; } }; diff --git a/src/tbb/def/lin32-tbb.def b/src/tbb/def/lin32-tbb.def index 3cc322f2a3..da1dc0e626 100644 --- a/src/tbb/def/lin32-tbb.def +++ b/src/tbb/def/lin32-tbb.def @@ -107,8 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEj; -_ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEj; +_ZN3tbb6detail2r119exit_parallel_phaseEPNS0_2d115task_arena_baseEj; +_ZN3tbb6detail2r120enter_parallel_phaseEPNS0_2d115task_arena_baseEj; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/src/tbb/def/lin64-tbb.def b/src/tbb/def/lin64-tbb.def index 9c0cdc1022..b14e1c805b 100644 --- a/src/tbb/def/lin64-tbb.def +++ b/src/tbb/def/lin64-tbb.def @@ -107,8 +107,8 @@ _ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE; _ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE; _ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE; _ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE; -_ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEm; -_ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEm; +_ZN3tbb6detail2r119exit_parallel_phaseEPNS0_2d115task_arena_baseEm; +_ZN3tbb6detail2r120enter_parallel_phaseEPNS0_2d115task_arena_baseEm; /* System topology parsing and threads pinning (governor.cpp) */ _ZN3tbb6detail2r115numa_node_countEv; diff --git a/src/tbb/def/mac64-tbb.def b/src/tbb/def/mac64-tbb.def index fe1daed52c..fff79a391d 100644 --- a/src/tbb/def/mac64-tbb.def +++ b/src/tbb/def/mac64-tbb.def @@ -109,8 +109,8 @@ __ZN3tbb6detail2r17enqueueERNS0_2d14taskEPNS2_15task_arena_baseE __ZN3tbb6detail2r17enqueueERNS0_2d14taskERNS2_18task_group_contextEPNS2_15task_arena_baseE __ZN3tbb6detail2r14waitERNS0_2d115task_arena_baseE __ZN3tbb6detail2r114execution_slotERKNS0_2d115task_arena_baseE -__ZN3tbb6detail2r123register_parallel_phaseEPNS0_2d115task_arena_baseEm -__ZN3tbb6detail2r125unregister_parallel_phaseEPNS0_2d115task_arena_baseEm +__ZN3tbb6detail2r119exit_parallel_phaseEPNS0_2d115task_arena_baseEm +__ZN3tbb6detail2r120enter_parallel_phaseEPNS0_2d115task_arena_baseEm # System topology parsing and threads pinning (governor.cpp) __ZN3tbb6detail2r115numa_node_countEv diff --git a/src/tbb/def/win32-tbb.def b/src/tbb/def/win32-tbb.def index 1ce826c120..516075afd0 100644 --- a/src/tbb/def/win32-tbb.def +++ b/src/tbb/def/win32-tbb.def @@ -101,8 +101,8 @@ EXPORTS ?wait@r1@detail@tbb@@YAXAAVtask_arena_base@d1@23@@Z ?enqueue@r1@detail@tbb@@YAXAAVtask@d1@23@AAVtask_group_context@523@PAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGABVtask_arena_base@d1@23@@Z -?register_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z -?unregister_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z +?enter_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z +?exit_parallel_phase@r1@detail@tbb@@YAXPAVtask_arena_base@d1@23@I@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ diff --git a/src/tbb/def/win64-tbb.def b/src/tbb/def/win64-tbb.def index e80029e976..80273f88a2 100644 --- a/src/tbb/def/win64-tbb.def +++ b/src/tbb/def/win64-tbb.def @@ -101,8 +101,8 @@ EXPORTS ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@PEAVtask_arena_base@523@@Z ?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@PEAVtask_arena_base@523@@Z ?execution_slot@r1@detail@tbb@@YAGAEBVtask_arena_base@d1@23@@Z -?register_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z -?unregister_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z +?enter_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z +?exit_parallel_phase@r1@detail@tbb@@YAXPEAVtask_arena_base@d1@23@_K@Z ; System topology parsing and threads pinning (governor.cpp) ?numa_node_count@r1@detail@tbb@@YAIXZ