From 26d7f293497dadeccd952ccbc840f6b5ce70763c Mon Sep 17 00:00:00 2001 From: Alexandra Date: Wed, 9 Oct 2024 18:12:28 +0200 Subject: [PATCH 01/37] Doc update for oneTBB 2022.0 (#1515) --- doc/GSG/get_started.rst | 27 ++++++++++++++++- doc/GSG/intro.rst | 29 ------------------- doc/index/toctree.rst | 1 - .../Exceptions_and_Cancellation.rst | 9 ++---- doc/main/tbb_userguide/Linux_OS.rst | 8 ++--- ...C_Dynamic_Memory_Interface_Replacement.rst | 4 +-- doc/main/tbb_userguide/Windows_OS_ug.rst | 10 +++---- 7 files changed, 39 insertions(+), 49 deletions(-) delete mode 100644 doc/GSG/intro.rst diff --git a/doc/GSG/get_started.rst b/doc/GSG/get_started.rst index d437ce89b8..2af04be6b0 100644 --- a/doc/GSG/get_started.rst +++ b/doc/GSG/get_started.rst @@ -8,11 +8,36 @@ It is helpful for new users of parallel programming and experienced developers t It is recommended for you to have a basic knowledge of C++ programming and some experience with parallel programming concepts. +|full_name| is a runtime-based parallel programming model for C++ code that uses tasks. +The template-based runtime library can help you harness the latent performance of multi-core processors. + +oneTBB enables you to simplify parallel programming by breaking computation into parallel running tasks. Within a single process, +parallelism is carried out by mapping tasks to threads. Threads are an operating system mechanism that allows the same or different sets of instructions +to be executed simultaneously. Using threads can make your program work faster and more efficiently. + +Here you can see one of the possible executions of tasks by threads. + +.. figure:: Images/how-oneTBB-works.png + :scale: 70% + :align: center + +Use oneTBB to write scalable applications that: + +* Specify logical parallel structure instead of threads. +* Emphasize data-parallel programming. +* Take advantage of concurrent collections and parallel algorithms. + +oneTBB supports nested parallelism and load balancing. It means that you can use the library without worrying about oversubscribing a system, which happens when more tasks are assigned to a system than it can handle efficiently. + +oneTBB is used in different areas, such as scientific simulations, gaming, data analysis, etc. + +It is available as a stand-alone product and as part of the |base_tk|. + To start using oneTBB, follow the next steps: ********************************************* -#. Learn what :ref:`oneTBB is` and see the :ref:`System Requirements`. +#. See the :ref:`System Requirements`. #. :ref:`Install oneTBB`. #. Run your program using oneTBB following the :ref:`Next Steps `. #. Learn how to :ref:`Integrate oneTBB into your project ` using CMake* and pkg-config tool. diff --git a/doc/GSG/intro.rst b/doc/GSG/intro.rst deleted file mode 100644 index da8c558d21..0000000000 --- a/doc/GSG/intro.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _intro: - -What oneTBB Is -============== - -|full_name| is a runtime-based parallel programming model for C++ code that uses threads. -The template-based runtime library can help you harness the latent performance of multi-core processors. - -oneTBB enables you to simplify parallel programming by breaking computation into parallel running tasks. Within a single process, -parallelism is carried out through threads, an operating system mechanism that allows the same or different sets of instructions -to be executed simultaneously. Using threads can make your program work faster and more efficiently. - -Here you can see one of the possible executions of tasks by threads. - -.. figure:: Images/how-oneTBB-works.png - :scale: 70% - :align: center - -Use oneTBB to write scalable applications that: - -* Specify logical parallel structure instead of threads. -* Emphasize data-parallel programming. -* Take advantage of concurrent collections and parallel algorithms. - -oneTBB supports nested parallelism and load balancing. It means that you can use the library without worrying about oversubscribing a system, which happens when more tasks are assigned to a system than it can handle efficiently. - -oneTBB is used in different areas, such as scientific simulations, gaming, data analysis, etc. - -It is available as a stand-alone product and as part of the |base_tk|. diff --git a/doc/index/toctree.rst b/doc/index/toctree.rst index fba9aee46c..542a4bb601 100644 --- a/doc/index/toctree.rst +++ b/doc/index/toctree.rst @@ -17,7 +17,6 @@ :maxdepth: 2 /GSG/get_started - /GSG/intro /GSG/system_requirements /GSG/installation /GSG/next_steps diff --git a/doc/main/tbb_userguide/Exceptions_and_Cancellation.rst b/doc/main/tbb_userguide/Exceptions_and_Cancellation.rst index 724b8b6ec9..290f2f2cc3 100644 --- a/doc/main/tbb_userguide/Exceptions_and_Cancellation.rst +++ b/doc/main/tbb_userguide/Exceptions_and_Cancellation.rst @@ -22,14 +22,11 @@ the following steps generally occur: thread that invoked the algorithm. -The exception thrown in step 3 might be the original exception, or might -merely be a summary of type ``captured_exception``. The latter usually -occurs on current systems because propagating exceptions between threads -requires support for the C++ ``std::exception_ptr`` functionality. As -compilers evolve to support this functionality, future versions of +As compilers evolve to support this functionality, future versions of oneTBB might throw the original exception. So be sure your code can catch either type of exception. The following example demonstrates -exception handling. +exception handling: + :: diff --git a/doc/main/tbb_userguide/Linux_OS.rst b/doc/main/tbb_userguide/Linux_OS.rst index 1d25a04dcd..0f0c245720 100644 --- a/doc/main/tbb_userguide/Linux_OS.rst +++ b/doc/main/tbb_userguide/Linux_OS.rst @@ -25,12 +25,12 @@ structure for Linux\*, relative to ** - | ``LIBRARY_PATH`` | ``LD_LIBRARY_PATH`` -where +Where: * ```` - ``ia32`` or ``intel64`` + + .. note:: Starting with oneTBB 2022.0, 32-bit binaries are supported only by the open-source version of the library. * ```` - ``libtbb``, ``libtbbmalloc``, ``libtbbmalloc_proxy`` or ``libtbbbind`` - * ```` - ``_debug`` or empty - -* ```` - binary version in a form of ``.`` \ No newline at end of file +* ```` - binary version in a form of ``.`` diff --git a/doc/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst b/doc/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst index f4f78ae567..cd2d2e1a93 100644 --- a/doc/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst +++ b/doc/main/tbb_userguide/Windows_C_Dynamic_Memory_Interface_Replacement.rst @@ -44,7 +44,6 @@ To do the replacement use one of the following methods: - Alternatively, add the following parameters to the linker options for the .exe or .dll file that is loaded during application startup. - For 32-bit code (note the triple underscore): @@ -52,8 +51,7 @@ To do the replacement use one of the following methods: tbbmalloc_proxy.lib /INCLUDE:"___TBB_malloc_proxy" - - + For 64-bit code (note the double underscore): diff --git a/doc/main/tbb_userguide/Windows_OS_ug.rst b/doc/main/tbb_userguide/Windows_OS_ug.rst index 3fc4a5a223..85fc3306ce 100644 --- a/doc/main/tbb_userguide/Windows_OS_ug.rst +++ b/doc/main/tbb_userguide/Windows_OS_ug.rst @@ -30,12 +30,13 @@ structure for Windows\*, relative to <*tbb_install_dir*>. - Same as corresponding ``.dll`` file. - \ -where +Where * ```` - ``ia32`` or ``intel64`` -* ```` - ``tbb``, ``tbbmalloc``, ``tbbmalloc_proxy`` or ``tbbbind`` + .. note:: Starting with oneTBB 2022.0, 32-bit binaries are supported only by the open-source version of the library. +* ```` - ``tbb``, ``tbbmalloc``, ``tbbmalloc_proxy`` or ``tbbbind`` * ```` - ``14`` - use for dynamic linkage with the CRT @@ -47,11 +48,10 @@ where - ``_mt`` - use for static linkage with the CRT * ```` - ``_debug`` or empty - * ```` - binary version -The last column shows which environment variables are used by the -Microsoft\* Visual C++\* or Intel® C++ Compiler Classic or Intel® oneAPI DPC++/C++ Compiler to find these +The last column shows, which environment variables are used by the +Microsoft\* Visual C++\* or Intel® C++ Compiler Classic or Intel® oneAPI DPC++/C++ Compiler, to find these subdirectories. .. CAUTION:: From 94bee9c86b4b342cd2eb46270f0c62b8edd4c76d Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Tue, 15 Oct 2024 13:44:41 +0200 Subject: [PATCH 02/37] Fix constraints_variety set traverse, extend constraints test cases (#1530) Signed-off-by: Isaev, Ilya --- test/common/common_arena_constraints.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/common/common_arena_constraints.h b/test/common/common_arena_constraints.h index 4f2da92022..22c0d05309 100644 --- a/test/common/common_arena_constraints.h +++ b/test/common/common_arena_constraints.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019-2023 Intel Corporation + Copyright (c) 2019-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. @@ -560,16 +560,19 @@ constraints_container generate_constraints_variety() { #endif /*__HYBRID_CPUS_TESTING*/ } + int max_threads_per_core = system_info::get_available_max_threads_values().back(); // Some constraints may cause unexpected behavior, which would be fixed later. if (get_processors_group_count() > 1) { - for(auto it = results.begin(); it != results.end(); ++it) { - if (it->max_threads_per_core != tbb::task_arena::automatic + for(auto it = results.begin(); it != results.end();) { + if (it->max_threads_per_core != max_threads_per_core && (it->numa_id == tbb::task_arena::automatic || tbb::info::numa_nodes().size() == 1) #if __HYBRID_CPUS_TESTING && (it->core_type == tbb::task_arena::automatic || tbb::info::core_types().size() == 1) #endif /*__HYBRID_CPUS_TESTING*/ ) { it = results.erase(it); + } else { + ++it; } } } From 42b833fe806606d05a5cad064b8b87365818d716 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Tue, 15 Oct 2024 07:58:21 -0400 Subject: [PATCH 03/37] Fix futex support on OpenBSD (#1499) OpenBSD no longer supports direct syscalls. --- src/tbb/semaphore.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tbb/semaphore.h b/src/tbb/semaphore.h index 9d27f3ac98..d4eadc2cf1 100644 --- a/src/tbb/semaphore.h +++ b/src/tbb/semaphore.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2022 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. @@ -98,7 +98,11 @@ namespace r1 { #if __TBB_USE_FUTEX static inline int futex_wait( void *futex, int comparand ) { +#ifdef __OpenBSD__ + int r = ::futex((volatile uint32_t *)futex, __TBB_FUTEX_WAIT, comparand, nullptr, nullptr); +#else int r = ::syscall(SYS_futex, futex, __TBB_FUTEX_WAIT, comparand, nullptr, nullptr, 0); +#endif #if TBB_USE_ASSERT int e = errno; __TBB_ASSERT(r == 0 || r == EWOULDBLOCK || (r == -1 && (e == EAGAIN || e == EINTR)), "futex_wait failed."); @@ -107,7 +111,11 @@ static inline int futex_wait( void *futex, int comparand ) { } static inline int futex_wakeup_one( void *futex ) { +#ifdef __OpenBSD__ + int r = ::futex((volatile uint32_t *)futex, __TBB_FUTEX_WAKE, 1 , nullptr, nullptr); +#else int r = ::syscall(SYS_futex, futex, __TBB_FUTEX_WAKE, 1, nullptr, nullptr, 0); +#endif __TBB_ASSERT(r == 0 || r == 1, "futex_wakeup_one: more than one thread woken up?"); return r; } From 377a91431ec62c5e296dbeca683c5d1e66d69f32 Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Wed, 23 Oct 2024 17:07:47 +0200 Subject: [PATCH 04/37] Remove sync-labels property from issue_labeler (#1534) --- .github/workflows/issue_labeler.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/issue_labeler.yml b/.github/workflows/issue_labeler.yml index 80591aa974..f410b2557d 100644 --- a/.github/workflows/issue_labeler.yml +++ b/.github/workflows/issue_labeler.yml @@ -34,4 +34,3 @@ jobs: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/issue_labeler.yml enable-versioned-regex: 0 - sync-labels: 1 From 948f0bbd4cbde2c7cbec2455bd0d75bed4ae6061 Mon Sep 17 00:00:00 2001 From: Dmitri Mokhov Date: Fri, 25 Oct 2024 09:42:08 -0500 Subject: [PATCH 05/37] Fix maximum concurrency on Windows with NUMA (#1538) Signed-off-by: Dmitri Mokhov --- examples/migration/recursive_fibonacci/task_emulation_layer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/migration/recursive_fibonacci/task_emulation_layer.h b/examples/migration/recursive_fibonacci/task_emulation_layer.h index 7252d447a0..e3b67b93e9 100644 --- a/examples/migration/recursive_fibonacci/task_emulation_layer.h +++ b/examples/migration/recursive_fibonacci/task_emulation_layer.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 Intel Corporation + Copyright (c) 2023-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. @@ -25,7 +25,7 @@ namespace task_emulation { struct task_group_pool { - task_group_pool() : pool_size(std::thread::hardware_concurrency()), task_submitters(new tbb::task_group[pool_size]) {} + task_group_pool() : pool_size(tbb::this_task_arena::max_concurrency()), task_submitters(new tbb::task_group[pool_size]) {} ~task_group_pool() { for (std::size_t i = 0; i < pool_size; ++i) { From 86c06983bff53a8c254a93cf85b7e9fe1da773dc Mon Sep 17 00:00:00 2001 From: Alexandra Date: Thu, 31 Oct 2024 12:42:28 +0100 Subject: [PATCH 06/37] Release Notes for 2022.0 (#1537) * Release Notes for 2022.0 * Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c9b8e97135..5e3b3d24bc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -18,8 +18,13 @@ This document contains changes of oneTBB compared to the last release. ## Table of Contents +- [Preview Features](#preview-features) - [Known Limitations](#known-limitations) -- [Fixed Issues](#fixed-issues) +- [Issues Fixed](#issues-fixed) +- [Open-Source Contributions Integrated](#open-source-contributions-integrated) + +## :tada: Preview Features +- Extended the Flow Graph receiving nodes with a new ``try_put_and_wait`` API that submits a message to the graph and waits for its completion. ## :rotating_light: Known Limitations - The ``oneapi::tbb::info`` namespace interfaces might unexpectedly change the process affinity mask on Windows* OS systems (see https://github.com/open-mpi/hwloc/issues/366 for details) when using hwloc version lower than 2.5. @@ -28,15 +33,19 @@ This document contains changes of oneTBB compared to the last release. - On Windows OS on ARM64*, when compiling an application using oneTBB with the Microsoft* Compiler, the compiler issues a warning C4324 that a structure was padded due to the alignment specifier. Consider suppressing the warning by specifying /wd4324 to the compiler command line. - C++ exception handling mechanism on Windows* OS on ARM64* might corrupt memory if an exception is thrown from any oneTBB parallel algorithm (see Windows* OS on ARM64* compiler issue: https://developercommunity.visualstudio.com/t/ARM64-incorrect-stack-unwinding-for-alig/1544293. - When CPU resource coordination is enabled, tasks from a lower-priority ``task_arena`` might be executed before tasks from a higher-priority ``task_arena``. +- Using oneTBB on WASM*, may cause applications to run in a single thread. See [Limitations of WASM Support](https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations). > **_NOTE:_** To see known limitations that impact all versions of oneTBB, refer to [oneTBB Documentation](https://oneapi-src.github.io/oneTBB/main/intro/limitations.html). -## :hammer: Fixed Issues -- Fixed ``parallel_for_each`` algorithm behavior for iterators defining ``iterator_concept`` trait instead of ``iterator_category``. -- Fixed the redefinition issue for ``std::min`` and ``std::max`` on Windows* OS ([GitHub* #832](https://github.com/oneapi-src/oneTBB/issues/832)). -- Fixed the incorrect binary search order in ``TBBConfig.cmake``. -- Enabled the oneTBB library search using the pkg-config tool in Conda packages. +## :hammer: Issues Fixed +- Fixed the missed signal for thread request for enqueue operation. +- Significantly improved scalability of ``task_group``, ``flow_graph``, and ``parallel_for_each``. +- Removed usage of ``std::aligned_storage`` deprecated in C++23 (Inspired by Valery Matskevich https://github.com/oneapi-src/oneTBB/pull/1394). +- Fixed the issue where ``oneapi::tbb::info`` interfaces might interfere with the process affinity mask on the Windows* OS systems with multiple processor groups. + -## :octocat: Open-source Contributions Integrated -- Fixed the compiler warning for missing virtual destructor. Contributed by Elias Engelbert Plank (https://github.com/oneapi-src/oneTBB/pull/1215). +## :octocat: Open-Source Contributions Integrated +- Detect the GNU Binutils version to determine WAITPKG support better. Contributed by Martijn Courteaux (https://github.com/oneapi-src/oneTBB/pull/1347). +- Fixed the build on non-English locales. Contributed by Vladislav Shchapov (https://github.com/oneapi-src/oneTBB/pull/1450). +- Improved Bazel support. Contributed by Julian Amann (https://github.com/oneapi-src/oneTBB/pull/1434). From bd15be276b27dcae39e48603596c8c8f9eba2f90 Mon Sep 17 00:00:00 2001 From: Olga Malysheva Date: Thu, 31 Oct 2024 14:49:14 +0100 Subject: [PATCH 07/37] Update upload-artifact to v4 (#1541) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dbf3c407d..b897224ea8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: export BUILD_TYPE=${BUILD_TYPE} && sphinx-build doc html tar -czvf html.tar.gz html/ - name: Save docs - uses: actions/upload-artifact@v2.2.1 + uses: actions/upload-artifact@v4 with: name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }} path: html.tar.gz From 9c9ace26863cf50853f39b2d0b78146d20a981a6 Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Fri, 1 Nov 2024 10:52:18 +0100 Subject: [PATCH 08/37] Fix flow_graph tests build when compiling with GCC 13.3 (#1543) Signed-off-by: Isaev, Ilya --- include/oneapi/tbb/detail/_flow_graph_impl.h | 2 +- include/oneapi/tbb/flow_graph.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/oneapi/tbb/detail/_flow_graph_impl.h b/include/oneapi/tbb/detail/_flow_graph_impl.h index 19e00a8ef1..55063b93e1 100644 --- a/include/oneapi/tbb/detail/_flow_graph_impl.h +++ b/include/oneapi/tbb/detail/_flow_graph_impl.h @@ -347,7 +347,7 @@ class graph : no_copy, public graph_proxy { caught_exception = false; try_call([this] { my_task_arena->execute([this] { - wait(my_wait_context_vertex.get_context(), *my_context); + d1::wait(my_wait_context_vertex.get_context(), *my_context); }); cancelled = my_context->is_group_execution_cancelled(); }).on_exception([this] { diff --git a/include/oneapi/tbb/flow_graph.h b/include/oneapi/tbb/flow_graph.h index 20916fa7c2..5b438faabf 100644 --- a/include/oneapi/tbb/flow_graph.h +++ b/include/oneapi/tbb/flow_graph.h @@ -305,7 +305,7 @@ class receiver { bool res = internal_try_put(t, message_metainfo{message_metainfo::waiters_type{&msg_wait_vertex}}); if (res) { __TBB_ASSERT(graph_reference().my_context != nullptr, "No wait_context associated with the Flow Graph"); - wait(msg_wait_vertex.get_context(), *graph_reference().my_context); + d1::wait(msg_wait_vertex.get_context(), *graph_reference().my_context); } return res; } From 73ccfb5ff34001504415530cbc2f66d706bb0bd1 Mon Sep 17 00:00:00 2001 From: sarathnandu Date: Mon, 4 Nov 2024 08:10:16 -0600 Subject: [PATCH 09/37] Added a utility class for Relative Error compute. (#1446) * Add a utility class measurements for relative error analysis. Co-authored-by: Aleksei Fedotov --- examples/common/utility/utility.hpp | 62 +++++++++++++++++++++++++- examples/parallel_for/seismic/main.cpp | 31 +++++++++++-- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/examples/common/utility/utility.hpp b/examples/common/utility/utility.hpp index 024f3e99c1..0630c81362 100644 --- a/examples/common/utility/utility.hpp +++ b/examples/common/utility/utility.hpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 Intel Corporation + Copyright (c) 2005-2024 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include // TBB headers should not be used, as some examples may need to be built without TBB. namespace utility { @@ -356,6 +358,59 @@ class cli_argument_pack { } }; // class cli_argument_pack +// utility class to aid relative error measurement of samples +class measurements { +public: + measurements() = default; + + measurements(unsigned iterations) { + _time_intervals.reserve(iterations); + } + + inline void start() { + _startTime = std::chrono::steady_clock::now(); + } + inline void stop() { + auto _endTime = std::chrono::steady_clock::now(); + // store the end time and start time + _time_intervals.push_back(std::make_pair(_startTime, _endTime)); + } + double computeRelError() { + // Accumulate the total duration in microseconds using std::accumulate with a lambda function + assert(0 != _time_intervals.size()); + auto total_duration = std::accumulate( + _time_intervals.begin(), + _time_intervals.end(), + 0, // Start with 0 count + [](long long total, const std::pair& interval) { + // Compute the difference and add it to the total + return total + std::chrono::duration_cast( + interval.second - interval.first) + .count(); + }); + unsigned long long averageTimePerFrame = total_duration / _time_intervals.size(); + unsigned long long sumOfSquareDiff = 0; + std::for_each(_time_intervals.begin(), + _time_intervals.end(), + [&](const std::pair& interval) { + unsigned long long duration = + std::chrono::duration_cast( + interval.second - interval.first) + .count(); + long long diff = duration - averageTimePerFrame; + sumOfSquareDiff += diff * diff; + }); + double stdDev = std::sqrt(sumOfSquareDiff / _time_intervals.size()); + double relError = 100 * (stdDev / averageTimePerFrame); + return relError; + } + +private: + using time_point = std::chrono::steady_clock::time_point; + time_point _startTime; + std::vector> _time_intervals; +}; + namespace internal { template bool is_power_of_2(T val) { @@ -547,6 +602,11 @@ inline void report_skipped() { << "\n"; } +inline void report_relative_error(double err) { + std::cout << "Relative_Err : " << err << " %" + << "\n"; +} + inline void parse_cli_arguments(int argc, const char* argv[], utility::cli_argument_pack cli_pack) { bool show_help = false; cli_pack.arg(show_help, "-h", "show this message"); diff --git a/examples/parallel_for/seismic/main.cpp b/examples/parallel_for/seismic/main.cpp index cc30bbd3b8..4f08de9342 100644 --- a/examples/parallel_for/seismic/main.cpp +++ b/examples/parallel_for/seismic/main.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 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. @@ -35,14 +35,17 @@ struct RunOptions { //! threads.second - initialization value for scheduler utility::thread_number_range threads; int numberOfFrames; + int numberOfIterations; bool silent; bool parallel; RunOptions(utility::thread_number_range threads_, int number_of_frames_, + int number_of_iterations_, bool silent_, bool parallel_) : threads(threads_), numberOfFrames(number_of_frames_), + numberOfIterations(number_of_iterations_), silent(silent_), parallel(parallel_) {} }; @@ -53,6 +56,7 @@ RunOptions ParseCommandLine(int argc, char *argv[]) { utility::get_default_num_threads, 0, utility::get_default_num_threads()); int numberOfFrames = 0; + int numberOfIterations = 0; bool silent = false; bool serial = false; @@ -65,15 +69,19 @@ RunOptions ParseCommandLine(int argc, char *argv[]) { .positional_arg(numberOfFrames, "n-of-frames", "number of frames the example processes internally (0 means unlimited)") + .positional_arg(numberOfIterations, + "n-of-iterations", + "number of iterations the example runs internally") .arg(silent, "silent", "no output except elapsed time") .arg(serial, "serial", "in GUI mode start with serial version of algorithm")); - return RunOptions(threads, numberOfFrames, silent, !serial); + return RunOptions(threads, numberOfFrames, numberOfIterations, silent, !serial); } int main(int argc, char *argv[]) { oneapi::tbb::tick_count mainStartTime = oneapi::tbb::tick_count::now(); RunOptions options = ParseCommandLine(argc, argv); SeismicVideo video(u, options.numberOfFrames, options.threads.last, options.parallel); + double rel_error; // video layer init if (video.init_window(u.UniverseWidth, u.UniverseHeight)) { @@ -91,11 +99,19 @@ int main(int argc, char *argv[]) { std::cout << "Substituting 1000 for unlimited frames because not running interactively" << "\n"; } + // TODO : Extend utility::cli_argument_pack() to allow specifying the default value. + if (options.numberOfIterations <= 0) { + options.numberOfIterations = 10; + std::cout << "Setting the number of iterations = 10 default" + << "\n"; + } for (int p = options.threads.first; p <= options.threads.last; p = options.threads.step(p)) { oneapi::tbb::tick_count xwayParallelismStartTime = oneapi::tbb::tick_count::now(); u.InitializeUniverse(video); int numberOfFrames = options.numberOfFrames; + assert(options.numberOfIterations > 0 && "Number of iterations cannot be <= 0"); + unsigned numberOfIterations = unsigned(options.numberOfIterations); if (p == 0) { //run a serial version @@ -106,9 +122,15 @@ int main(int argc, char *argv[]) { else { oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, p); - for (int i = 0; i < numberOfFrames; ++i) { - u.ParallelUpdateUniverse(); + utility::measurements mu(numberOfIterations); + for (int iter = 0; iter < numberOfIterations; ++iter) { + mu.start(); + for (int i = 0; i < numberOfFrames; ++i) { + u.ParallelUpdateUniverse(); + } + mu.stop(); } + rel_error = mu.computeRelError(); } if (!options.silent) { @@ -129,5 +151,6 @@ int main(int argc, char *argv[]) { } video.terminate(); utility::report_elapsed_time((oneapi::tbb::tick_count::now() - mainStartTime).seconds()); + utility::report_relative_error(rel_error); return 0; } From 63a2a4dae3d0faba44b1ef02810e7bf079f8241a Mon Sep 17 00:00:00 2001 From: Aleksei Fedotov Date: Mon, 4 Nov 2024 15:59:46 +0100 Subject: [PATCH 10/37] Fix race in `concurrent_vector::grow_by()` (#1532) Signed-off-by: Fedotov, Aleksei --- include/oneapi/tbb/concurrent_vector.h | 6 +-- include/oneapi/tbb/detail/_segment_table.h | 47 ++++++++++++++-------- test/tbb/test_concurrent_vector.cpp | 39 ++++++++++++++---- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/include/oneapi/tbb/concurrent_vector.h b/include/oneapi/tbb/concurrent_vector.h index 2a2cb1e4bf..27cdc47355 100644 --- a/include/oneapi/tbb/concurrent_vector.h +++ b/include/oneapi/tbb/concurrent_vector.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2022 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. @@ -593,7 +593,7 @@ class concurrent_vector segment_type disabled_segment = nullptr; if (table[0].compare_exchange_strong(disabled_segment, new_segment)) { - this->extend_table_if_necessary(table, 0, first_block_size); + this->extend_table_if_necessary(table, /*start_index*/0, /*end_index*/first_block_size); for (size_type i = 1; i < first_block; ++i) { table[i].store(new_segment, std::memory_order_release); } @@ -826,8 +826,8 @@ class concurrent_vector template iterator internal_grow( size_type start_idx, size_type end_idx, const Args&... args ) { - this->assign_first_block_if_necessary(this->segment_index_of(end_idx - 1) + 1); size_type seg_index = this->segment_index_of(end_idx - 1); + this->assign_first_block_if_necessary(seg_index + 1); segment_table_type table = this->get_table(); this->extend_table_if_necessary(table, start_idx, end_idx); diff --git a/include/oneapi/tbb/detail/_segment_table.h b/include/oneapi/tbb/detail/_segment_table.h index 1a31d8a17d..7fbf1cc0f9 100644 --- a/include/oneapi/tbb/detail/_segment_table.h +++ b/include/oneapi/tbb/detail/_segment_table.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2022 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. @@ -260,34 +260,49 @@ class segment_table { } } + void destroy_and_deallocate_table(segment_table_type table, size_type num_segments) { + auto& alloc = get_allocator(); + for (size_type seg_idx = 0; seg_idx < num_segments; ++seg_idx) { + segment_table_allocator_traits::destroy(alloc, &table[seg_idx]); + } + segment_table_allocator_traits::deallocate(alloc, table, num_segments); + } + void clear_table() { segment_table_type current_segment_table = get_table(); if (current_segment_table != my_embedded_table) { // If the active table is not the embedded one - deallocate the active table - for (size_type i = 0; i != pointers_per_long_table; ++i) { - segment_table_allocator_traits::destroy(my_segment_table_allocator, ¤t_segment_table[i]); - } - - segment_table_allocator_traits::deallocate(my_segment_table_allocator, current_segment_table, pointers_per_long_table); + destroy_and_deallocate_table(current_segment_table, pointers_per_long_table); my_segment_table.store(my_embedded_table, std::memory_order_relaxed); zero_table(my_embedded_table, pointers_per_embedded_table); } } void extend_table_if_necessary(segment_table_type& table, size_type start_index, size_type end_index) { - // extend_segment_table if an active table is an embedded table - // and the requested index is not in the embedded table + // Extend segment table if an active table is an embedded one and the requested index is + // outside it if (table == my_embedded_table && end_index > embedded_table_size) { if (start_index <= embedded_table_size) { + // More than one thread can get here: the one that has assigned the first block and + // is in the process of allocating it now, and the one that saw the first block has + // been assigned already, but not yet allocated. This latter thread decides not to + // wait for the first one and extend the table itself. try_call([&] { - table = self()->allocate_long_table(my_embedded_table, start_index); - // It is possible that the table was extended by the thread that allocated first_block. - // In this case it is necessary to re-read the current table. - - if (table) { - my_segment_table.store(table, std::memory_order_release); - } else { - table = my_segment_table.load(std::memory_order_acquire); + segment_table_type new_table = + self()->allocate_long_table(my_embedded_table, start_index); + // It is possible that the table was extended by the thread that allocated first + // block. In this case, the below CAS fails and re-reads the new table pointer. + if (my_segment_table.compare_exchange_strong( + table, new_table, + /*memory order in case of a success*/std::memory_order_release, + /*memory order in case of a failure*/std::memory_order_acquire)) + { + // CAS was successful, update the local table pointer with now actual + table = new_table; + } else if (new_table) { + // Other thread was the first to replace the segment table. Current thread's + // table is not needed anymore, so destroying it. + destroy_and_deallocate_table(new_table, pointers_per_long_table); } }).on_exception([&] { my_segment_table_allocation_failed.store(true, std::memory_order_relaxed); diff --git a/test/tbb/test_concurrent_vector.cpp b/test/tbb/test_concurrent_vector.cpp index afb37ab49c..0237102e17 100644 --- a/test/tbb/test_concurrent_vector.cpp +++ b/test/tbb/test_concurrent_vector.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2022 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. @@ -33,6 +33,7 @@ #include #include #include +#include //! \file test_concurrent_vector.cpp //! \brief Test for [containers.concurrent_vector] specification @@ -692,14 +693,36 @@ TEST_CASE("swap with not always equal allocators"){ // or fail with the assertion in debug mode. //! \brief \ref regression TEST_CASE("Testing vector in a highly concurrent environment") { - for (std::size_t i = 0; i < 10000; ++i) { - tbb::concurrent_vector test_vec; - - tbb::parallel_for(tbb::blocked_range(0, 10000), [&] (const tbb::blocked_range&) { - test_vec.grow_by(1); - }, tbb::static_partitioner{}); + std::uniform_int_distribution<> uniform_dist(1, 32); // grow by from 1 to 32 randomly + std::mt19937_64 gen(/*seed*/1); // Constructing with seed to have reproducible results + constexpr int num_repeats = 10000, num_inserts = 256; + std::vector grow_by_vals(num_inserts); + + for (int i = 0; i < num_repeats; ++i) { + int expected_size = 0, expected_sum = 0; + std::generate(grow_by_vals.begin(), grow_by_vals.end(), + [&gen, &uniform_dist, &expected_size, &expected_sum]() { + const int random_value = uniform_dist(gen); + expected_size += random_value; + expected_sum += random_value * random_value; + return random_value; + }); - REQUIRE(test_vec.size() == utils::get_platform_max_threads()); + tbb::concurrent_vector test_vec; + tbb::parallel_for(0, num_inserts, [&] (int j) { + tbb::concurrent_vector::iterator start_it = test_vec.grow_by(grow_by_vals[j]); + tbb::concurrent_vector::iterator end_it = start_it + grow_by_vals[j]; + do { + *start_it = grow_by_vals[j]; + } while (++start_it != end_it); + }); + + REQUIRE(test_vec.size() == expected_size); + int actual_sum = 0; + for (int j = 0; j < expected_size; ++j) { + actual_sum += test_vec[j]; + } + REQUIRE(expected_sum == actual_sum); } } From 22a5be70950ec42e94c47fc9e14d6aa2794af749 Mon Sep 17 00:00:00 2001 From: Olga Malysheva Date: Tue, 5 Nov 2024 15:08:35 +0100 Subject: [PATCH 11/37] Add dependabot.yml to enable automatic check for GitHub actions version update (#1546) --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..5ace4600a1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From afad98a16600fbabb79601ecc1cc62bc75ad0199 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:51:09 +0000 Subject: [PATCH 12/37] Bump ossf/scorecard-action from 2.3.1 to 2.4.0 (#1547) --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 9f45569f8a..e03dbb750a 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -47,7 +47,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@v2.3.1 + uses: ossf/scorecard-action@v2.4.0 with: results_file: results.sarif results_format: sarif From 3874ffd4fbc3061ac6993a3081129e402a8aa8fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:52:45 +0000 Subject: [PATCH 13/37] Bump actions/labeler from 4 to 5 (#1548) --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 36812ebd62..31d150a8fa 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -24,5 +24,5 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 From 9b5ca7493ce523dbe69787fd0cfec366039a90fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:54:55 +0000 Subject: [PATCH 14/37] Bump step-security/harden-runner from 2.6.1 to 2.10.1 (#1550) --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7a80c5f0e2..3ea8258480 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.6.1 + uses: step-security/harden-runner@v2.10.1 with: egress-policy: audit From eae64c1c412a0a0f56cb8e9df41754bdbdf75c8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:57:24 +0000 Subject: [PATCH 15/37] Bump actions/download-artifact from 2 to 4 (#1549) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b897224ea8..f7ccfa2590 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,7 +97,7 @@ jobs: - name: Set env run: echo GITHUB_SHA_SHORT=${GITHUB_SHA::8} >> $GITHUB_ENV - name: Download documetation - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }} - name: Publish to github pages From ae2b801c3f349381f1060c881ddbd9893d97f7f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:59:13 +0000 Subject: [PATCH 16/37] Bump github/issue-labeler from 3.2 to 3.4 (#1551) --- .github/workflows/issue_labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_labeler.yml b/.github/workflows/issue_labeler.yml index f410b2557d..1f8e9f78bc 100644 --- a/.github/workflows/issue_labeler.yml +++ b/.github/workflows/issue_labeler.yml @@ -29,7 +29,7 @@ jobs: issues: write contents: read steps: - - uses: github/issue-labeler@v3.2 #May not be the latest version + - uses: github/issue-labeler@v3.4 #May not be the latest version with: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/issue_labeler.yml From d1d43adf6af117d3ab4ee3a14a5fa129e0690290 Mon Sep 17 00:00:00 2001 From: tobiasweinzierl80 Date: Thu, 7 Nov 2024 10:29:36 +0000 Subject: [PATCH 17/37] Move blocked_nd_range out of preview (#1449) --------- Signed-off-by: Tobias Weinzierl Co-authored-by: Johannes Bockhorst Co-authored-by: Alexey Kukanov Co-authored-by: Konstantin Boyarinov Co-authored-by: pavelkumbrasev --- include/oneapi/tbb.h | 6 +- .../{blocked_rangeNd.h => blocked_nd_range.h} | 78 ++++++++++--------- include/oneapi/tbb/blocked_range.h | 4 +- include/oneapi/tbb/detail/_template_helpers.h | 6 +- .../{blocked_rangeNd.h => blocked_nd_range.h} | 4 +- test/CMakeLists.txt | 2 +- test/common/config.h | 5 +- ...d.cpp => conformance_blocked_nd_range.cpp} | 64 +++++++++------ test/tbb/test_blocked_range.cpp | 19 +++-- test/tbb/test_hw_concurrency.cpp | 7 +- test/tbb/test_tbb_header.cpp | 2 +- 11 files changed, 109 insertions(+), 88 deletions(-) rename include/oneapi/tbb/{blocked_rangeNd.h => blocked_nd_range.h} (58%) rename include/tbb/{blocked_rangeNd.h => blocked_nd_range.h} (86%) rename test/conformance/{conformance_blocked_rangeNd.cpp => conformance_blocked_nd_range.cpp} (79%) diff --git a/include/oneapi/tbb.h b/include/oneapi/tbb.h index ad96011373..c52eb9e228 100644 --- a/include/oneapi/tbb.h +++ b/include/oneapi/tbb.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. @@ -28,9 +28,7 @@ #include "oneapi/tbb/blocked_range.h" #include "oneapi/tbb/blocked_range2d.h" #include "oneapi/tbb/blocked_range3d.h" -#if TBB_PREVIEW_BLOCKED_RANGE_ND -#include "tbb/blocked_rangeNd.h" -#endif +#include "oneapi/tbb/blocked_nd_range.h" #include "oneapi/tbb/cache_aligned_allocator.h" #include "oneapi/tbb/combinable.h" #include "oneapi/tbb/concurrent_hash_map.h" diff --git a/include/oneapi/tbb/blocked_rangeNd.h b/include/oneapi/tbb/blocked_nd_range.h similarity index 58% rename from include/oneapi/tbb/blocked_rangeNd.h rename to include/oneapi/tbb/blocked_nd_range.h index a7ba137506..3a9697896f 100644 --- a/include/oneapi/tbb/blocked_rangeNd.h +++ b/include/oneapi/tbb/blocked_nd_range.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2017-2021 Intel Corporation + Copyright (c) 2017-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. @@ -14,12 +14,8 @@ limitations under the License. */ -#ifndef __TBB_blocked_rangeNd_H -#define __TBB_blocked_rangeNd_H - -#if !TBB_PREVIEW_BLOCKED_RANGE_ND - #error Set TBB_PREVIEW_BLOCKED_RANGE_ND to include blocked_rangeNd.h -#endif +#ifndef __TBB_blocked_nd_range_H +#define __TBB_blocked_nd_range_H #include // std::any_of #include @@ -28,6 +24,7 @@ #include "detail/_config.h" #include "detail/_template_helpers.h" // index_sequence, make_index_sequence +#include "detail/_namespace_injection.h" #include "detail/_range_common.h" #include "blocked_range.h" @@ -37,45 +34,56 @@ namespace detail { namespace d1 { /* - The blocked_rangeNd_impl uses make_index_sequence to automatically generate a ctor with + The blocked_nd_range_impl uses make_index_sequence to automatically generate a ctor with exactly N arguments of the type tbb::blocked_range. Such ctor provides an opportunity to use braced-init-list parameters to initialize each dimension. Use of parameters, whose representation is a braced-init-list, but they're not std::initializer_list or a reference to one, produces a non-deduced context within template argument deduction. - NOTE: blocked_rangeNd must be exactly a templated alias to the blocked_rangeNd_impl + NOTE: blocked_nd_range must be exactly a templated alias to the blocked_nd_range_impl (and not e.g. a derived class), otherwise it would need to declare its own ctor facing the same problem that the impl class solves. */ template> __TBB_requires(blocked_range_value) -class blocked_rangeNd_impl; +class blocked_nd_range_impl; template __TBB_requires(blocked_range_value) -class blocked_rangeNd_impl> { +class blocked_nd_range_impl> { public: //! Type of a value. using value_type = Value; -private: - //! Helper type to construct range with N tbb::blocked_range objects. - template - using dim_type_helper = tbb::blocked_range; + //! Type of a dimension range. + using dim_range_type = tbb::blocked_range; -public: - blocked_rangeNd_impl() = delete; + //! Type for the size of a range. + using size_type = typename dim_range_type::size_type; + + blocked_nd_range_impl() = delete; //! Constructs N-dimensional range over N half-open intervals each represented as tbb::blocked_range. - blocked_rangeNd_impl(const dim_type_helper&... args) : my_dims{ {args...} } {} + blocked_nd_range_impl(const indexed_t&... args) : my_dims{ {args...} } {} + +#if __clang__ && __TBB_CLANG_VERSION < 140000 + // On clang prior to version 14.0.0, passing a single braced init list to the constructor of blocked_nd_range + // matches better on the C array constructor and generates compile-time error because of unexpected size + // Adding constraints for this constructor to force the compiler to drop it from overload resolution if the size is unexpected + template ::type> + blocked_nd_range_impl(const value_type (&size)[M], size_type grainsize = 1) : +#else + blocked_nd_range_impl(const value_type (&size)[N], size_type grainsize = 1) : +#endif + my_dims { dim_range_type(0, size[Is], grainsize)... } {} //! Dimensionality of a range. - static constexpr unsigned int ndims() { return N; } + static constexpr unsigned int dim_count() { return N; } //! Range in certain dimension. - const tbb::blocked_range& dim(unsigned int dimension) const { + const dim_range_type& dim(unsigned int dimension) const { __TBB_ASSERT(dimension < N, "out of bound"); return my_dims[dimension]; } @@ -86,44 +94,45 @@ class blocked_rangeNd_impl> { //! True if at least one dimension is empty. bool empty() const { - return std::any_of(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& d) { + return std::any_of(my_dims.begin(), my_dims.end(), [](const dim_range_type& d) { return d.empty(); }); } //! True if at least one dimension is divisible. bool is_divisible() const { - return std::any_of(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& d) { + return std::any_of(my_dims.begin(), my_dims.end(), [](const dim_range_type& d) { return d.is_divisible(); }); } - blocked_rangeNd_impl(blocked_rangeNd_impl& r, proportional_split proportion) : my_dims(r.my_dims) { + blocked_nd_range_impl(blocked_nd_range_impl& r, proportional_split proportion) : my_dims(r.my_dims) { do_split(r, proportion); } - blocked_rangeNd_impl(blocked_rangeNd_impl& r, split proportion) : my_dims(r.my_dims) { + blocked_nd_range_impl(blocked_nd_range_impl& r, split proportion) : my_dims(r.my_dims) { do_split(r, proportion); } private: - static_assert(N != 0, "zero dimensional blocked_rangeNd can't be constructed"); + static_assert(N != 0, "zero dimensional blocked_nd_range can't be constructed"); //! Ranges in each dimension. - std::array, N> my_dims; + std::array my_dims; template - void do_split(blocked_rangeNd_impl& r, split_type proportion) { - static_assert((std::is_same::value || std::is_same::value), "type of split object is incorrect"); + void do_split(blocked_nd_range_impl& r, split_type proportion) { + static_assert((std::is_same::value || std::is_same::value), + "type of split object is incorrect"); __TBB_ASSERT(r.is_divisible(), "can't split not divisible range"); - auto my_it = std::max_element(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& first, const tbb::blocked_range& second) { - return (first.size() * second.grainsize() < second.size() * first.grainsize()); + auto my_it = std::max_element(my_dims.begin(), my_dims.end(), [](const dim_range_type& first, const dim_range_type& second) { + return (first.size() * double(second.grainsize()) < second.size() * double(first.grainsize())); }); auto r_it = r.my_dims.begin() + (my_it - my_dims.begin()); - my_it->my_begin = tbb::blocked_range::do_split(*r_it, proportion); + my_it->my_begin = dim_range_type::do_split(*r_it, proportion); // (!(my_it->my_begin < r_it->my_end) && !(r_it->my_end < my_it->my_begin)) equals to // (my_it->my_begin == r_it->my_end), but we can't use operator== due to Value concept @@ -133,15 +142,14 @@ class blocked_rangeNd_impl> { }; template -using blocked_rangeNd = blocked_rangeNd_impl; +using blocked_nd_range = blocked_nd_range_impl; } // namespace d1 } // namespace detail inline namespace v1 { -using detail::d1::blocked_rangeNd; +using detail::d1::blocked_nd_range; } // namespace v1 } // namespace tbb -#endif /* __TBB_blocked_rangeNd_H */ - +#endif /* __TBB_blocked_nd_range_H */ diff --git a/include/oneapi/tbb/blocked_range.h b/include/oneapi/tbb/blocked_range.h index 12862fa2a1..5193faffd5 100644 --- a/include/oneapi/tbb/blocked_range.h +++ b/include/oneapi/tbb/blocked_range.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 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. @@ -152,7 +152,7 @@ class blocked_range { template __TBB_requires(blocked_range_value) - friend class blocked_rangeNd_impl; + friend class blocked_nd_range_impl; }; } // namespace d1 diff --git a/include/oneapi/tbb/detail/_template_helpers.h b/include/oneapi/tbb/detail/_template_helpers.h index 50ce3d2d3b..a20c5af5c3 100644 --- a/include/oneapi/tbb/detail/_template_helpers.h +++ b/include/oneapi/tbb/detail/_template_helpers.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. @@ -106,6 +106,10 @@ using make_index_sequence = typename make_index_sequence_impl::type; #endif /* __TBB_CPP14_INTEGER_SEQUENCE_PRESENT */ +//! Attach an index to a type to use it with an index sequence +template +using indexed_t = T; + #if __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT using std::conjunction; using std::disjunction; diff --git a/include/tbb/blocked_rangeNd.h b/include/tbb/blocked_nd_range.h similarity index 86% rename from include/tbb/blocked_rangeNd.h rename to include/tbb/blocked_nd_range.h index 0c0fb7303a..70ca73af4b 100644 --- a/include/tbb/blocked_rangeNd.h +++ b/include/tbb/blocked_nd_range.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2017-2021 Intel Corporation + Copyright (c) 2017-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. @@ -14,4 +14,4 @@ limitations under the License. */ -#include "../oneapi/tbb/blocked_rangeNd.h" +#include "../oneapi/tbb/blocked_nd_range.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fb4a78bdbb..0ab4d7e8c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -537,7 +537,7 @@ if (TARGET TBB::tbb) tbb_add_test(SUBDIR conformance NAME conformance_blocked_range DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR conformance NAME conformance_blocked_range2d DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR conformance NAME conformance_blocked_range3d DEPENDENCIES TBB::tbb) - tbb_add_test(SUBDIR conformance NAME conformance_blocked_rangeNd DEPENDENCIES TBB::tbb) + tbb_add_test(SUBDIR conformance NAME conformance_blocked_nd_range DEPENDENCIES TBB::tbb) tbb_add_test(SUBDIR conformance NAME conformance_concurrent_vector DEPENDENCIES TBB::tbb) if (NOT TBB_TCM_TESTING) tbb_add_test(SUBDIR conformance NAME conformance_global_control DEPENDENCIES TBB::tbb) diff --git a/test/common/config.h b/test/common/config.h index aa75790c3f..c7ff8ba63a 100644 --- a/test/common/config.h +++ b/test/common/config.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 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. @@ -36,9 +36,6 @@ #ifndef TBB_PREVIEW_VARIADIC_PARALLEL_INVOKE #define TBB_PREVIEW_VARIADIC_PARALLEL_INVOKE 1 #endif -#ifndef TBB_PREVIEW_BLOCKED_RANGE_ND -#define TBB_PREVIEW_BLOCKED_RANGE_ND 1 -#endif #ifndef TBB_PREVIEW_ISOLATED_TASK_GROUP #define TBB_PREVIEW_ISOLATED_TASK_GROUP 1 #endif diff --git a/test/conformance/conformance_blocked_rangeNd.cpp b/test/conformance/conformance_blocked_nd_range.cpp similarity index 79% rename from test/conformance/conformance_blocked_rangeNd.cpp rename to test/conformance/conformance_blocked_nd_range.cpp index 52faac52ca..999eacb3c6 100644 --- a/test/conformance/conformance_blocked_rangeNd.cpp +++ b/test/conformance/conformance_blocked_nd_range.cpp @@ -19,11 +19,10 @@ #include "common/utils_assert.h" #include "common/utils_concurrency_limit.h" -//! \file conformance_blocked_rangeNd.cpp -//! \brief Test for [preview] functionality +//! \file conformance_blocked_nd_range.cpp +//! \brief Test for [algorithms.blocked_nd_range] specification -#define TBB_PREVIEW_BLOCKED_RANGE_ND 1 -#include "oneapi/tbb/blocked_rangeNd.h" +#include "oneapi/tbb/blocked_nd_range.h" #include "oneapi/tbb/parallel_for.h" #include "oneapi/tbb/global_control.h" @@ -160,10 +159,10 @@ int MakeInt(int i) { return i; } template void SerialTest() { - static_assert((oneapi::tbb::blocked_rangeNd::ndims() == oneapi::tbb::blocked_rangeNd::ndims()), + static_assert((oneapi::tbb::blocked_nd_range::dim_count() == oneapi::tbb::blocked_nd_range::dim_count()), "different amount of dimensions"); - using range_t = oneapi::tbb::blocked_rangeNd; + using range_t = oneapi::tbb::blocked_nd_range; using utils_t = range_utils; // Generate empty range @@ -171,7 +170,7 @@ void SerialTest() { utils::AssertSameType(r.is_divisible(), bool()); utils::AssertSameType(r.empty(), bool()); - utils::AssertSameType(range_t::ndims(), 0U); + utils::AssertSameType(range_t::dim_count(), 0U); REQUIRE((r.empty() == utils_t::is_empty(r) && r.empty())); REQUIRE(r.is_divisible() == utils_t::is_divisible(r)); @@ -190,7 +189,7 @@ template<> void SerialTest<0>() {} template void ParallelTest() { - using range_t = oneapi::tbb::blocked_rangeNd; + using range_t = oneapi::tbb::blocked_nd_range; using utils_t = range_utils; // Max size is 1 << 20 - 1 bytes @@ -210,28 +209,45 @@ void ParallelTest() { } template<> void ParallelTest<0>() {} -//! Testing blocked_rangeNd construction +//! Testing blocked_nd_range construction //! \brief \ref interface TEST_CASE("Construction") { - oneapi::tbb::blocked_rangeNd{ { 0,13,3 } }; + oneapi::tbb::blocked_nd_range{ { 0,13,3 } }; - oneapi::tbb::blocked_rangeNd{ oneapi::tbb::blocked_range{ 0,13,3 } }; + oneapi::tbb::blocked_nd_range{ oneapi::tbb::blocked_range{ 0,13,3 } }; - oneapi::tbb::blocked_rangeNd(oneapi::tbb::blocked_range(-8923, 8884, 13), oneapi::tbb::blocked_range(-8923, 5, 13)); + oneapi::tbb::blocked_nd_range(oneapi::tbb::blocked_range(-8923, 8884, 13), oneapi::tbb::blocked_range(-8923, 5, 13)); - oneapi::tbb::blocked_rangeNd({ -8923, 8884, 13 }, { -8923, 8884, 13 }); + oneapi::tbb::blocked_nd_range({ -8923, 8884, 13 }, { -8923, 8884, 13 }); oneapi::tbb::blocked_range r1(0, 13); oneapi::tbb::blocked_range r2(-12, 23); - oneapi::tbb::blocked_rangeNd({ { -8923, 8884, 13 }, r1}); + oneapi::tbb::blocked_nd_range({ { -8923, 8884, 13 }, r1}); - oneapi::tbb::blocked_rangeNd({ r2, r1 }); + oneapi::tbb::blocked_nd_range({ r2, r1 }); - oneapi::tbb::blocked_rangeNd(r1, r2); + oneapi::tbb::blocked_nd_range(r1, r2); - oneapi::tbb::blocked_rangeNd({ MakeAbstractValue(-3), MakeAbstractValue(13), 8 }, + int sizes[] = {174, 39, 2481, 93}; + oneapi::tbb::blocked_nd_range rNd_1(sizes, /*grainsize*/7); + + oneapi::tbb::blocked_nd_range rNd_2({174, 39, 2481, 93}, /*grainsize*/11); + + for (unsigned i = 0; i < rNd_1.dim_count(); ++i) { + oneapi::tbb::blocked_nd_range::dim_range_type dim1 = rNd_1.dim(i); + oneapi::tbb::blocked_nd_range::dim_range_type dim2 = rNd_2.dim(i); + REQUIRE(dim1.begin()==0); + REQUIRE(dim2.begin()==0); + unsigned int szi = sizes[i]; // to compare with unsigned integrals without warnings + REQUIRE(dim1.size()==szi); + REQUIRE(dim2.size()==szi); + REQUIRE(dim1.grainsize()==7); + REQUIRE(dim2.grainsize()==11); + } + + oneapi::tbb::blocked_nd_range({ MakeAbstractValue(-3), MakeAbstractValue(13), 8 }, { MakeAbstractValue(-53), MakeAbstractValue(23), 2 }, { MakeAbstractValue(-23), MakeAbstractValue(33), 1 }, { MakeAbstractValue(-13), MakeAbstractValue(43), 7 }); @@ -239,14 +255,14 @@ TEST_CASE("Construction") { static const std::size_t N = 4; -//! Testing blocked_rangeNd interface +//! Testing blocked_nd_range interface //! \brief \ref interface \ref requirement TEST_CASE("Serial test") { SerialTest(); } #if !EMSCRIPTEN -//! Testing blocked_rangeNd interface with parallel_for +//! Testing blocked_nd_range interface with parallel_for //! \brief \ref requirement TEST_CASE("Parallel test") { for ( auto concurrency_level : utils::concurrency_range() ) { @@ -256,13 +272,13 @@ TEST_CASE("Parallel test") { } #endif -//! Testing blocked_rangeNd with proportional splitting +//! Testing blocked_nd_range with proportional splitting //! \brief \ref interface \ref requirement -TEST_CASE("blocked_rangeNd proportional splitting") { - oneapi::tbb::blocked_rangeNd original{{0, 100}, {0, 100}}; - oneapi::tbb::blocked_rangeNd first(original); +TEST_CASE("blocked_nd_range proportional splitting") { + oneapi::tbb::blocked_nd_range original{{0, 100}, {0, 100}}; + oneapi::tbb::blocked_nd_range first(original); oneapi::tbb::proportional_split ps(3, 1); - oneapi::tbb::blocked_rangeNd second(first, ps); + oneapi::tbb::blocked_nd_range second(first, ps); int expected_first_end = static_cast( original.dim(0).begin() + ps.left() * (original.dim(0).end() - original.dim(0).begin()) / (ps.left() + ps.right()) diff --git a/test/tbb/test_blocked_range.cpp b/test/tbb/test_blocked_range.cpp index 651122220a..2443d590e1 100644 --- a/test/tbb/test_blocked_range.cpp +++ b/test/tbb/test_blocked_range.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 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. @@ -24,11 +24,10 @@ #include "tbb/blocked_range.h" #include "tbb/blocked_range2d.h" #include "tbb/blocked_range3d.h" -#define TBB_PREVIEW_BLOCKED_RANGE_ND 1 -#include "tbb/blocked_rangeNd.h" +#include "tbb/blocked_nd_range.h" //! \file test_blocked_range.cpp -//! \brief Test for [algorithms.blocked_range] specification +//! \brief Test for [algorithms.blocked_range algorithms.blocked_range2d algorithms.blocked_range3d algorithms.blocked_nd_range] specification #include //for std::pair #include @@ -120,12 +119,12 @@ template void test_blocked_range3d_col_invalid_constraint() {} template -concept well_formed_blocked_range_Nd_instantiation_basic = requires { - typename tbb::blocked_rangeNd; +concept well_formed_blocked_nd_range_instantiation_basic = requires { + typename tbb::blocked_nd_range; }; template -concept well_formed_blocked_range_Nd_instantiation = ( ... && well_formed_blocked_range_Nd_instantiation_basic ); +concept well_formed_blocked_nd_range_instantiation = ( ... && well_formed_blocked_nd_range_instantiation_basic ); //! \brief \ref error_guessing TEST_CASE("constraints for blocked_range value") { @@ -180,13 +179,13 @@ TEST_CASE("constraints for blocked_range3d value") { } //! \brief \ref error_guessing -TEST_CASE("constraints for blocked_rangeNd value") { +TEST_CASE("constraints for blocked_nd_range value") { using namespace test_concepts::blocked_range_value; using const_iterator = typename std::vector::const_iterator; - static_assert(well_formed_blocked_range_Nd_instantiation); + static_assert(well_formed_blocked_nd_range_instantiation); - static_assert(!well_formed_blocked_range_Nd_instantiation); diff --git a/test/tbb/test_hw_concurrency.cpp b/test/tbb/test_hw_concurrency.cpp index 16d4067b83..115a8f34be 100644 --- a/test/tbb/test_hw_concurrency.cpp +++ b/test/tbb/test_hw_concurrency.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2005-2021 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. @@ -47,8 +47,7 @@ #include "tbb/blocked_range.h" #include "tbb/blocked_range2d.h" #include "tbb/blocked_range3d.h" -#define TBB_PREVIEW_BLOCKED_RANGE_ND 1 -#include "tbb/blocked_rangeNd.h" +#include "tbb/blocked_nd_range.h" // Declaration of global objects are needed to check that // it does not initialize the task scheduler, and in particular @@ -87,7 +86,7 @@ tbb::tick_count test_tc; tbb::blocked_range br(0, 1); tbb::blocked_range2d br2d(0, 1, 0, 1); tbb::blocked_range3d br3d(0, 1, 0, 1, 0, 1); -tbb::blocked_rangeNd brNd({0, 1}, {0, 1}); +tbb::blocked_nd_range brNd({0, 1}, {0, 1}); //! \brief \ref error_guessing TEST_CASE("Check absence of scheduler initialization") { diff --git a/test/tbb/test_tbb_header.cpp b/test/tbb/test_tbb_header.cpp index a7b0791ef7..122af5ec08 100644 --- a/test/tbb/test_tbb_header.cpp +++ b/test/tbb/test_tbb_header.cpp @@ -172,7 +172,6 @@ static void TestExceptionClassesExports () { // These names are only tested in "preview" configuration // When a feature becomes fully supported, its names should be moved to the main test static void TestPreviewNames() { - TestTypeDefinitionPresence2( blocked_rangeNd ); TestTypeDefinitionPresence2( concurrent_lru_cache ); TestTypeDefinitionPresence( isolated_task_group ); } @@ -238,6 +237,7 @@ static void DefinitionPresence() { TestTypeDefinitionPresence( blocked_range ); TestTypeDefinitionPresence( blocked_range2d ); TestTypeDefinitionPresence( blocked_range3d ); + TestTypeDefinitionPresence2( blocked_nd_range ); TestTypeDefinitionPresence( collaborative_once_flag ); TestFuncDefinitionPresence( collaborative_call_once, (tbb::collaborative_once_flag&, const Body&), void ); TestFuncDefinitionPresence( parallel_invoke, (const Body&, const Body&, const Body&), void ); From f5a5f79a333123e5cadbaf7f7f56f84f81f2e525 Mon Sep 17 00:00:00 2001 From: Olga Malysheva Date: Mon, 18 Nov 2024 15:16:20 +0100 Subject: [PATCH 18/37] Adjust labeler config file (#1563) * Adjust labeler config file * Fix labeler workflow --- .github/labeler.yml | 7 +++---- .github/workflows/labeler.yml | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 8d13d71446..839d7f36c0 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Intel Corporation +# Copyright (c) 2023-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. @@ -13,6 +13,5 @@ # limitations under the License. allocator: -- 'src/tbbmalloc/**/*' -- 'src/tbbmalloc_proxy/**/*' -- 'test/tbbmalloc/**/*' +- changed-files: + - any-glob-to-any-file: ['src/tbbmalloc/**/*', 'src/tbbmalloc_proxy/**/*', 'test/tbbmalloc/**/*'] diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 31d150a8fa..770c211780 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -24,5 +24,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: actions/labeler@v5 - + with: + configuration-path: .github/labeler.yml From 0d545e2865886cc47886341a60ef916bb674e3b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:57:59 +0000 Subject: [PATCH 19/37] Bump step-security/harden-runner in /.github/workflows (#1565) --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3ea8258480..28d3ff62b1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.10.1 + uses: step-security/harden-runner@v2.10.2 with: egress-policy: audit From bd291eb0d1c7f802ca5081a8755562ab786db2a2 Mon Sep 17 00:00:00 2001 From: Ilya Isaev Date: Fri, 22 Nov 2024 10:31:52 +0100 Subject: [PATCH 20/37] Fix module show when using Lmod (#1567) --- integration/linux/modulefiles/tbb | 4 ++-- integration/linux/modulefiles/tbb32 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration/linux/modulefiles/tbb b/integration/linux/modulefiles/tbb index b8c695ed2c..58113ee62c 100644 --- a/integration/linux/modulefiles/tbb +++ b/integration/linux/modulefiles/tbb @@ -1,6 +1,6 @@ #%Module1.0################################################################### # -# Copyright (c) 2020-2023 Intel Corporation +# Copyright (c) 2020-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. @@ -50,7 +50,7 @@ module-whatis "Dependencies: none" proc ModulesHelp { } { global modulefilename global modulefilever - module whatis "${modulefilename}/${modulefilever}" + puts "module whatis ${modulefilename}/${modulefilever}" } ############################################################################## diff --git a/integration/linux/modulefiles/tbb32 b/integration/linux/modulefiles/tbb32 index db34135176..89d6bc60fe 100644 --- a/integration/linux/modulefiles/tbb32 +++ b/integration/linux/modulefiles/tbb32 @@ -1,6 +1,6 @@ #%Module1.0################################################################### # -# Copyright (c) 2020-2023 Intel Corporation +# Copyright (c) 2020-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. @@ -50,7 +50,7 @@ module-whatis "Dependencies: none" proc ModulesHelp { } { global modulefilename global modulefilever - module whatis "${modulefilename}/${modulefilever}" + puts "module whatis ${modulefilename}/${modulefilever}" } ############################################################################## From bef1519a4216d77042637c3f48af2c060a5213d1 Mon Sep 17 00:00:00 2001 From: Pavel Kumbrasev Date: Mon, 25 Nov 2024 12:22:07 +0000 Subject: [PATCH 21/37] Introduce MAINTAINERS.md (#1560) --------- Signed-off-by: pavelkumbrasev Co-authored-by: Ilya Isaev Co-authored-by: Mike Voss Co-authored-by: Aleksei Fedotov Co-authored-by: Alexandra --- MAINTAINERS.md | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 MAINTAINERS.md diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000000..99c0a1dc92 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,143 @@ + + +# Introduction + +This document defines roles in the oneTBB project. + +# Roles and Responsibilities + +oneTBB project defines three main roles: + * [Contributor](#contributor) + * [Code Owner](#code-Owner) + * [Maintainer](#maintainer) + +[permissions]: https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization#permissions-for-each-role + +| | Contributor | Code Owner | Maintainer | +| :------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------: | :---------------------: | :---------------------: | +| _Responsibilities_ | | | | +| Follow the [Code of Conduct](./CODE_OF_CONDUCT.md) | ✓ | ✓ | ✓ | +| Follow [Contribution Guidelines](./CONTRIBUTING.md) | ✓ | ✓ | ✓ | +| Ensure [Contribution Guidelines](./CONTRIBUTING.md) are followed | ✗ | ✓ | ✓ | +| Co-own component or aspect of the library,
including contributing: bug fixes, implementing features,
and performance optimizations | ✗ | ✓ | ✓ | +| Co-own on technical direction of component or
aspect of the library including work on RFCs | ✗ | ✓ | ✓ | +| Co-own the project as a whole,
including determining strategy and policy for the project | ✗ | ✗ | ✓ | +| _Privileges_ | | | | +| Permission granted | [Read][permissions] | [Write][permissions] | [Maintain][permissions] | +| Eligible to become | Code Owner | Maintainer | ✗ | +| Can recommend Contributors
to become Code Owner | ✗ | ✓ | ✓ | +| Can participate in promotions of
Code Owners and Maintainers | ✗ | ✗ | ✓ | +| Can suggest Milestones during planning | ✓ | ✓ | ✓ | +| Can choose Milestones for specific component | ✗ | ✓ | ✓ | +| Make a decision on project's Milestones during planning | ✗ | ✗ | ✓ | +| Can propose new RFC or
participate in review of existing RFC | ✓ | ✓ | ✓ | +| Can request rework of RFCs
in represented area of responsibility | ✗ | ✓ | ✓ | +| Can request rework of RFCs
in any part of the project | ✗ | ✗ | ✓ | +| Can manage release process of the project | ✗ | ✗ | ✓ | +| Can represent the project in public as a Maintainer | ✗ | ✗ | ✓ | + +These roles are merit based. Refer to the corresponding section for specific +requirements and the nomination process. + +## Contributor + +A Contributor invests time and resources to improve oneTBB project. +Anyone can become a Contributor by bringing value in any following way: + * Answer questions from community members. + * Propose changes to the design. + * Provide feedback on design proposals. + * Review and/or test pull requests. + * Test releases and report bugs. + * Contribute code, including bug fixes, features implementations, +and performance optimizations. + +## Code Owner + +A Code Owner has responsibility for a specific project component or a functional +area. Code Owners are collectively responsible +for developing and maintaining their component or functional areas, including +reviewing all changes to corresponding areas of responsibility and indicating +whether those changes are ready to be merged. Code Owners have a track record of +contribution and review in the project. + +**Requirements:** + * Track record of accepted code contributions to a specific project component. + * Track record of contributions to the code review process. + * Demonstrate in-depth knowledge of the architecture of a specific project + component. + * Commit to being responsible for that specific area. + +How to become a Code Owner? +1. A Contributor is nominated by opening a PR modifying the MAINTAINERS.md file +including name, Github username, and affiliation. +2. At least two specific component Maintainers approve the PR. +3. [CODEOWNERS](./CODEOWNERS) file is updated to represent corresponding areas of responsibility. + +## Maintainer +Maintainers are the most established contributors responsible for the +project technical direction. They participate in making decisions about the +strategy and priorities of the project. + +**Requirements:** + * Have experience as a Code Owner. + * Track record of major project contributions to a specific project component. + * Demonstrate deep knowledge of a specific project component. + * Demonstrate broad knowledge of the project across multiple areas. + * Commit to using privileges responsibly for the good of the project. + * Be able to exercise judgment for the good of the project, independent of + their employer, friends, or team. + +Process of becoming a maintainer: +1. A Maintainer may nominate a current code owner to become a new Maintainer by +opening a PR against MAINTAINERS.md file. +2. A majority of the current Maintainers must then approve the PR. + +# Code Owners and Maintainers List + +## oneTBB core (API, Architecture, Tests) + +| Name | Github ID | Affiliation | Role | +| --------------------- | --------------------- | ----------------- | ---------- | +| Ilya Isaev | @isaevil | Intel Corporation | Code Owner | +| Sarath Nandu R | @sarathnandu | Intel Corporation | Code Owner | +| Dmitri Mokhov | @dnmokhov | Intel Corporation | Code Owner | +| Alexey Kukanov | @akukanov | Intel Corporation | Code Owner | +| Konstantin Boyarinov | @kboyarinov | Intel Corporation | Maintainer | +| Aleksei Fedotov | @aleksei-fedotov | Intel Corporation | Maintainer | +| Michael Voss | @vossmjp | Intel Corporation | Maintainer | +| Pavel Kumbrasev | @pavelkumbrasev | Intel Corporation | Maintainer | + +## oneTBB TBBMALLOC (API, Architecture, Tests) + +| Name | Github ID | Affiliation | Role | +| --------------------- | --------------------- | ----------------- | ---------- | +| Łukasz Plewa | @lplewa | Intel Corporation | Maintainer | + + +## oneTBB Documentation + +| Name | Github ID | Affiliation | Role | +| ---------------------- | --------------------- | ----------------- | ---------- | +| Alexandra Epanchinzeva | @aepanchi | Intel Corporation | Code Owner | + + +## oneTBB Release Management + +| Name | Github ID | Affiliation | Role | +| ------------------ | --------------------- | ----------------- | ---------- | +| Olga Malysheva | @omalyshe | Intel Corporation | Maintainer | + From 0d377cc8ec08d549f29ea1f2ac1b64bba9267505 Mon Sep 17 00:00:00 2001 From: Alexey Kukanov Date: Thu, 28 Nov 2024 16:25:24 +0100 Subject: [PATCH 22/37] Extend the documentation with more information about multidimensional ranges (#1569) Co-authored-by: Alexandra --- ..._Topic_Other_Kinds_of_Iteration_Spaces.rst | 38 ++++++++++++++++++- doc/main/tbb_userguide/parallel_for_os.rst | 5 ++- .../snippets/blocked_nd_range_example.cpp | 37 ++++++++++++++++++ .../snippets/blocked_nd_range_example.h | 37 ++++++++++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 doc/main/tbb_userguide/snippets/blocked_nd_range_example.cpp create mode 100644 doc/main/tbb_userguide/snippets/blocked_nd_range_example.h diff --git a/doc/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst b/doc/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst index 3352dd8d32..99446ab659 100644 --- a/doc/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst +++ b/doc/main/tbb_userguide/Advanced_Topic_Other_Kinds_of_Iteration_Spaces.rst @@ -72,4 +72,40 @@ along its longest axis. When used with ``parallel_for``, it causes the loop to be "recursively blocked" in a way that improves cache usage. This nice cache behavior means that using ``parallel_for`` over a ``blocked_range2d`` can make a loop run faster than the sequential -equivalent, even on a single processor. +equivalent, even on a single processor. + +The ``blocked_range2d`` allows you to use different value types for +its first dimension, *rows*, and the second one, *columns*. +That means you can combine indexes, pointers, and iterators into a joint +iteration space. Use the methods ``rows()`` and ``cols()`` to obtain +``blocked_range`` objects that represent the respective dimensions. + +The ``blocked_range3d`` class template extends this approach to 3D by adding +``pages()`` as the first dimension, followed by ``rows()`` and ``cols()``. + +The ``blocked_nd_range`` class template represents a blocked iteration +space of any dimensionality. Unlike the previously described 2D and 3D ranges, +``blocked_nd_range`` uses the same value type for all its axes, and its +constructor requires you to pass N instances of ``blocked_range`` instead of +individual boundary values. The change in the naming pattern reflects these +differences. + + +Example of a Multidimensional Iteration Space +------------------------------------------------ + +The example demonstrates calculation of a 3-dimensional filter over the pack +of feature maps. + +The ``convolution3d`` function iterates over the output cells, assigning to +each cell the result of the ``kernel3d`` function that combines the values +from a range in the feature maps. + +To run the computation in parallel, ``tbb::parallel_for`` is called with +``tbb::blocked_nd_range`` as an argument. The body function processes +the received 3D subrange in nested loops, using the method ``dim`` to get +the loop boundaries for each dimension. + + +.. literalinclude:: ./snippets/blocked_nd_range_example.h + :language: c++ diff --git a/doc/main/tbb_userguide/parallel_for_os.rst b/doc/main/tbb_userguide/parallel_for_os.rst index fed07af68b..cbc7578f4c 100644 --- a/doc/main/tbb_userguide/parallel_for_os.rst +++ b/doc/main/tbb_userguide/parallel_for_os.rst @@ -55,8 +55,9 @@ before each identifier. The rest of the examples assume that such a Note the argument to ``operator()``. A ``blocked_range`` is a template class provided by the library. It describes a one-dimensional iteration space over type ``T``. Class ``parallel_for`` works with other -kinds of iteration spaces too. The library provides ``blocked_range2d`` -for two-dimensional spaces. You can define your own spaces as explained +kinds of iteration spaces too. The library provides ``blocked_range2d``, +``blocked_range3d``, and ``blocked_nd_range`` for multidimensional spaces. +You can define your own spaces as explained in :ref:`Advanced_Topic_Other_Kinds_of_Iteration_Spaces`. diff --git a/doc/main/tbb_userguide/snippets/blocked_nd_range_example.cpp b/doc/main/tbb_userguide/snippets/blocked_nd_range_example.cpp new file mode 100644 index 0000000000..7417123999 --- /dev/null +++ b/doc/main/tbb_userguide/snippets/blocked_nd_range_example.cpp @@ -0,0 +1,37 @@ +#include "blocked_nd_range_example.h" +#include +#include + +int main() { + const int kernel_length = 9; + const int kernel_width = 5; + const int kernel_height = 5; + + const int feature_maps_length = 128; + const int feature_maps_width = 16; + const int feature_maps_heigth = 16; + + const int out_length = feature_maps_length - kernel_length + 1; + const int out_width = feature_maps_width - kernel_width + 1; + const int out_heigth = feature_maps_heigth - kernel_height + 1; + + // Initializes feature maps with 1 in each cell and out with zeros. + std::vector>> feature_maps(feature_maps_length, std::vector>(feature_maps_width, std::vector(feature_maps_heigth, 1.0f))); + std::vector>> out(out_length, std::vector>(out_width, std::vector(out_heigth, 0.f))); + + // 3D convolution calculates the sum of all elements in the kernel + convolution3d(feature_maps, out, + out_length, out_width, out_heigth, + kernel_length, kernel_width, kernel_height); + + // Checks correctness of convolution by equality to the expected sum of elements + float expected = float(kernel_length * kernel_height * kernel_width); + for (auto i : out) { + for (auto j : i) { + for (auto k : j) { + assert(k == expected && "convolution failed to calculate correctly"); + } + } + } + return 0; +} diff --git a/doc/main/tbb_userguide/snippets/blocked_nd_range_example.h b/doc/main/tbb_userguide/snippets/blocked_nd_range_example.h new file mode 100644 index 0000000000..ded2a09c57 --- /dev/null +++ b/doc/main/tbb_userguide/snippets/blocked_nd_range_example.h @@ -0,0 +1,37 @@ +#include "oneapi/tbb/blocked_nd_range.h" +#include "oneapi/tbb/parallel_for.h" + +template +float kernel3d(const Features& feature_maps, int i, int j, int k, + int kernel_length, int kernel_width, int kernel_height) { + float result = 0.f; + + for (int feature_i = i; feature_i < i + kernel_length; ++feature_i) + for (int feature_j = j; feature_j < j + kernel_width; ++feature_j) + for (int feature_k = k; feature_k < k + kernel_width; ++feature_k) + result += feature_maps[feature_i][feature_j][feature_k]; + + return result; +} + +template +void convolution3d(const Features& feature_maps, Output& out, + int out_length, int out_width, int out_heigth, + int kernel_length, int kernel_width, int kernel_height) { + using range_t = oneapi::tbb::blocked_nd_range; + + oneapi::tbb::parallel_for( + range_t({0, out_length}, {0, out_width}, {0, out_heigth}), + [&](const range_t& out_range) { + auto out_x = out_range.dim(0); + auto out_y = out_range.dim(1); + auto out_z = out_range.dim(2); + + for (int i = out_x.begin(); i < out_x.end(); ++i) + for (int j = out_y.begin(); j < out_y.end(); ++j) + for (int k = out_z.begin(); k < out_z.end(); ++k) + out[i][j][k] = kernel3d(feature_maps, i, j, k, + kernel_length, kernel_width, kernel_height); + } + ); +} From d3b0a80d02501b2304de6afb940465f465482a6b Mon Sep 17 00:00:00 2001 From: snadampal <87143774+snadampal@users.noreply.github.com> Date: Fri, 29 Nov 2024 02:17:51 -0600 Subject: [PATCH 23/37] Bazel: fix build issue on arm64 linux platform (#1571) --- BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/BUILD.bazel b/BUILD.bazel index 9073f4640d..7c479ca3ed 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -37,6 +37,7 @@ cc_library( ]), copts = ["-w"] + select({ "@platforms//os:windows": [""], + "@platforms//cpu:arm64": [""], "//conditions:default": ["-mwaitpkg"], }), defines = From db883d1d0f6c5a0152fc21f146c06f9c02bb2f88 Mon Sep 17 00:00:00 2001 From: FantasqueX Date: Tue, 3 Dec 2024 19:16:06 +0800 Subject: [PATCH 24/37] Improve code readability in CMake integration section (#1574) --- doc/GSG/integrate.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/GSG/integrate.rst b/doc/GSG/integrate.rst index 13fd679dab..2b38dba246 100644 --- a/doc/GSG/integrate.rst +++ b/doc/GSG/integrate.rst @@ -26,10 +26,10 @@ Integrating oneTBB into your project using CMake*: To add oneTBB to another project using CMake*, add the following commands to your ``CMakeLists.txt`` file: -.. code-block:: +.. code-block:: cmake - `find_package(TBB REQUIRED)` - `target_link_libraries(my_executable TBB::tbb)` + find_package(TBB REQUIRED) + target_link_libraries(my_executable TBB::tbb) After that, configure your project with CMake* as usual. From 90b0f18297235511a86971653dc1dafc30ff4104 Mon Sep 17 00:00:00 2001 From: Olga Malysheva Date: Thu, 5 Dec 2024 16:49:25 +0100 Subject: [PATCH 25/37] Fix broken links after migration to uxlfoundation org. (#1577) --- Bazel.md | 2 +- CONTRIBUTING.md | 8 ++--- INSTALL.md | 6 ++-- README.md | 14 ++++----- RELEASE_NOTES.md | 12 ++++---- SECURITY.md | 6 ++-- SUPPORT.md | 4 +-- WASM_Support.md | 2 +- cmake/README.md | 4 +-- doc/GSG/installation.rst | 2 +- doc/GSG/samples.rst | 44 +++++++++++++-------------- doc/GSG/system_requirements.rst | 2 +- doc/README.md | 2 +- doc/conf.py | 6 ++-- doc/main/intro/help_support.rst | 2 +- doc/main/tbb_userguide/std_invoke.rst | 22 +++++++------- examples/README.md | 4 +-- integration/pkg-config/tbb.pc.in | 4 +-- 18 files changed, 73 insertions(+), 73 deletions(-) diff --git a/Bazel.md b/Bazel.md index 09a630a72b..13b112ee81 100644 --- a/Bazel.md +++ b/Bazel.md @@ -40,7 +40,7 @@ load("@platforms//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "oneTBB", branch = "master", - remote = "https://github.com/oneapi-src/oneTBB/", + remote = "https://github.com/uxlfoundation/oneTBB/", ) ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b2b6a968cd..3414b9eaf6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,17 +19,17 @@ As an open source project, we welcome community contributions to oneAPI Threadin ## Licensing -Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/oneapi-src/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms. +Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/uxlfoundation/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms. -Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/oneapi-src/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues. +Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/uxlfoundation/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues. ## Prerequisites -As a contributor, you’ll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://oneapi-src.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md). +As a contributor, you'll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://uxlfoundation.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md). ## Pull Requests -You can find all [open oneTBB pull requests](https://github.com/oneapi-src/oneTBB/pulls) on GitHub. +You can find all [open oneTBB pull requests](https://github.com/uxlfoundation/oneTBB/pulls) on GitHub. ### Before contributing changes directly to the oneTBB repository diff --git a/INSTALL.md b/INSTALL.md index 0ac95f8755..c33a2c7293 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ You can use the ``install`` components for partial installation. The following install components are supported: - `runtime` - oneTBB runtime package (core shared libraries and `.dll` files on Windows* OS). - `devel` - oneTBB development package (header files, CMake integration files, library symbolic links, and `.lib` files on Windows* OS). -- `tbb4py` - [oneTBB Module for Python](https://github.com/oneapi-src/oneTBB/blob/master/python/README.md). +- `tbb4py` - [oneTBB Module for Python](https://github.com/uxlfoundation/oneTBB/blob/master/python/README.md). If you want to install specific components after configuration and build, run: @@ -99,7 +99,7 @@ The following example demonstrates how to install oneTBB for single-configuratio # Do our experiments in /tmp cd /tmp # Clone oneTBB repository -git clone https://github.com/oneapi-src/oneTBB.git +git clone https://github.com/uxlfoundation/oneTBB.git cd oneTBB # Create binary directory for out-of-source build mkdir build && cd build @@ -121,7 +121,7 @@ Choose the configuration during the build and install steps: REM Do our experiments in %TMP% cd %TMP% REM Clone oneTBB repository -git clone https://github.com/oneapi-src/oneTBB.git +git clone https://github.com/uxlfoundation/oneTBB.git cd oneTBB REM Create binary directory for out-of-source build mkdir build && cd build diff --git a/README.md b/README.md index 3a0b50a7c0..36bdd6ce26 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # oneAPI Threading Building Blocks (oneTBB) -[![Apache License Version 2.0](https://img.shields.io/badge/license-Apache_2.0-green.svg)](LICENSE.txt) [![oneTBB CI](https://github.com/oneapi-src/oneTBB/actions/workflows/ci.yml/badge.svg)](https://github.com/oneapi-src/oneTBB/actions/workflows/ci.yml?query=branch%3Amaster) -[![Join the community on GitHub Discussions](https://badgen.net/badge/join%20the%20discussion/on%20github/blue?icon=github)](https://github.com/oneapi-src/oneTBB/discussions) +[![Apache License Version 2.0](https://img.shields.io/badge/license-Apache_2.0-green.svg)](LICENSE.txt) [![oneTBB CI](https://github.com/uxlfoundation/oneTBB/actions/workflows/ci.yml/badge.svg)](https://github.com/uxlfoundation/oneTBB/actions/workflows/ci.yml?query=branch%3Amaster) +[![Join the community on GitHub Discussions](https://badgen.net/badge/join%20the%20discussion/on%20github/blue?icon=github)](https://github.com/uxlfoundation/oneTBB/discussions) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9125/badge)](https://www.bestpractices.dev/projects/9125) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/oneapi-src/oneTBB/badge)](https://securityscorecards.dev/viewer/?uri=github.com/oneapi-src/oneTBB) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/uxlfoundation/oneTBB/badge)](https://securityscorecards.dev/viewer/?uri=github.com/uxlfoundation/oneTBB) oneTBB is a flexible C++ library that simplifies the work of adding parallelism to complex applications, even if you are not a threading expert. @@ -31,12 +31,12 @@ See [Release Notes](RELEASE_NOTES.md) and [System Requirements](SYSTEM_REQUIREME ## Documentation * [oneTBB Specification](https://spec.oneapi.com/versions/latest/elements/oneTBB/source/nested-index.html) -* [oneTBB Developer Guide and Reference](https://oneapi-src.github.io/oneTBB) -* [Migrating from TBB to oneTBB](https://oneapi-src.github.io/oneTBB/main/tbb_userguide/Migration_Guide.html) +* [oneTBB Developer Guide and Reference](https://uxlfoundation.github.io/oneTBB) +* [Migrating from TBB to oneTBB](https://uxlfoundation.github.io/oneTBB/main/tbb_userguide/Migration_Guide.html) * [README for the CMake build system](cmake/README.md) -* [oneTBB Testing Approach](https://oneapi-src.github.io/oneTBB/main/intro/testing_approach.html) +* [oneTBB Testing Approach](https://uxlfoundation.github.io/oneTBB/main/intro/testing_approach.html) * [Basic support for the Bazel build system](Bazel.md) -* [oneTBB Discussions](https://github.com/oneapi-src/oneTBB/discussions) +* [oneTBB Discussions](https://github.com/uxlfoundation/oneTBB/discussions) * [WASM Support](WASM_Support.md) ## Installation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5e3b3d24bc..16f04e0b92 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -33,19 +33,19 @@ This document contains changes of oneTBB compared to the last release. - On Windows OS on ARM64*, when compiling an application using oneTBB with the Microsoft* Compiler, the compiler issues a warning C4324 that a structure was padded due to the alignment specifier. Consider suppressing the warning by specifying /wd4324 to the compiler command line. - C++ exception handling mechanism on Windows* OS on ARM64* might corrupt memory if an exception is thrown from any oneTBB parallel algorithm (see Windows* OS on ARM64* compiler issue: https://developercommunity.visualstudio.com/t/ARM64-incorrect-stack-unwinding-for-alig/1544293. - When CPU resource coordination is enabled, tasks from a lower-priority ``task_arena`` might be executed before tasks from a higher-priority ``task_arena``. -- Using oneTBB on WASM*, may cause applications to run in a single thread. See [Limitations of WASM Support](https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations). +- Using oneTBB on WASM*, may cause applications to run in a single thread. See [Limitations of WASM Support](https://github.com/uxlfoundation/oneTBB/blob/master/WASM_Support.md#limitations). -> **_NOTE:_** To see known limitations that impact all versions of oneTBB, refer to [oneTBB Documentation](https://oneapi-src.github.io/oneTBB/main/intro/limitations.html). +> **_NOTE:_** To see known limitations that impact all versions of oneTBB, refer to [oneTBB Documentation](https://uxlfoundation.github.io/oneTBB/main/intro/limitations.html). ## :hammer: Issues Fixed - Fixed the missed signal for thread request for enqueue operation. - Significantly improved scalability of ``task_group``, ``flow_graph``, and ``parallel_for_each``. -- Removed usage of ``std::aligned_storage`` deprecated in C++23 (Inspired by Valery Matskevich https://github.com/oneapi-src/oneTBB/pull/1394). +- Removed usage of ``std::aligned_storage`` deprecated in C++23 (Inspired by Valery Matskevich https://github.com/uxlfoundation/oneTBB/pull/1394). - Fixed the issue where ``oneapi::tbb::info`` interfaces might interfere with the process affinity mask on the Windows* OS systems with multiple processor groups. ## :octocat: Open-Source Contributions Integrated -- Detect the GNU Binutils version to determine WAITPKG support better. Contributed by Martijn Courteaux (https://github.com/oneapi-src/oneTBB/pull/1347). -- Fixed the build on non-English locales. Contributed by Vladislav Shchapov (https://github.com/oneapi-src/oneTBB/pull/1450). -- Improved Bazel support. Contributed by Julian Amann (https://github.com/oneapi-src/oneTBB/pull/1434). +- Detect the GNU Binutils version to determine WAITPKG support better. Contributed by Martijn Courteaux (https://github.com/uxlfoundation/oneTBB/pull/1347). +- Fixed the build on non-English locales. Contributed by Vladislav Shchapov (https://github.com/uxlfoundation/oneTBB/pull/1450). +- Improved Bazel support. Contributed by Julian Amann (https://github.com/uxlfoundation/oneTBB/pull/1434). diff --git a/SECURITY.md b/SECURITY.md index 4926041fc2..c9be5beb7f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -61,6 +61,6 @@ If you have any suggestions on how this Policy could be improved, submit an issue or a pull request to this repository. **Do not** report potential vulnerabilities or security flaws via a pull request. -[1]: https://github.com/oneapi-src/oneTBB/releases/latest -[2]: https://github.com/oneapi-src/oneTBB/security/advisories/new -[3]: https://github.com/oneapi-src/oneTBB/security/advisories +[1]: https://github.com/uxlfoundation/oneTBB/releases/latest +[2]: https://github.com/uxlfoundation/oneTBB/security/advisories/new +[3]: https://github.com/uxlfoundation/oneTBB/security/advisories diff --git a/SUPPORT.md b/SUPPORT.md index 47bb60a538..bbf24f2854 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -21,14 +21,14 @@ Use the following methods if you face any challenges. ## Issues -If you have a problem, check out the [GitHub Issues](https://github.com/oneapi-src/oneTBB/issues) to see if the issue you want to address is already reported. +If you have a problem, check out the [GitHub Issues](https://github.com/uxlfoundation/oneTBB/issues) to see if the issue you want to address is already reported. You may find users that have encountered the same bug or have similar ideas for changes or updates. You can use issues to report a problem, make a feature request, or add comments on an existing issue. ## Discussions -Visit the [GitHub Discussions](https://github.com/oneapi-src/oneTBB/discussions) to engage with the community, ask questions, or help others. +Visit the [GitHub Discussions](https://github.com/uxlfoundation/oneTBB/discussions) to engage with the community, ask questions, or help others. ## Email diff --git a/WASM_Support.md b/WASM_Support.md index 6306620d7c..f40cf38c3d 100644 --- a/WASM_Support.md +++ b/WASM_Support.md @@ -47,7 +47,7 @@ Where: * ``-DCMAKE_C_COMPILER=emcc`` - specifies the C compiler as Emscripten* C compiler. -> **_NOTE:_** See [CMake documentation](https://github.com/oneapi-src/oneTBB/blob/master/cmake/README.md) to learn about other options. +> **_NOTE:_** See [CMake documentation](https://github.com/uxlfoundation/oneTBB/blob/master/cmake/README.md) to learn about other options. ## Run Test diff --git a/cmake/README.md b/cmake/README.md index 93710218cd..5883b0a655 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -48,7 +48,7 @@ Some useful options: #### TBBBind Library Configuration -> **_TIP:_** It is recommended to install the HWLOC* library. See [oneTBB documentation](https://oneapi-src.github.io/oneTBB/GSG/next_steps.html#hybrid-cpu-and-numa-support) for details. +> **_TIP:_** It is recommended to install the HWLOC* library. See [oneTBB documentation](https://uxlfoundation.github.io/oneTBB/GSG/next_steps.html#hybrid-cpu-and-numa-support) for details. The TBBbind library has three versions: `tbbbind`, `tbbbind_2_0`, and `tbbbind_2_5`. Each of these versions is linked with the corresponding HWLOC* library version: - `tbbbind` links with `HWLOC 1.11.x` @@ -270,7 +270,7 @@ Variable | Description `TBB_VERSION` | oneTBB version (format: `...`) `TBB_IMPORTED_TARGETS` | All created oneTBB imported targets (not supported for builds from source code) -Starting from [oneTBB 2021.1](https://github.com/oneapi-src/oneTBB/releases/tag/v2021.1), GitHub* release TBBConfig files in the binary packages are located under `/lib/cmake/TBB`. +Starting from [oneTBB 2021.1](https://github.com/uxlfoundation/oneTBB/releases/tag/v2021.1), GitHub* release TBBConfig files in the binary packages are located under `/lib/cmake/TBB`. For example, `TBB_DIR` should be set to `/lib/cmake/TBB`. TBBConfig files are automatically created during the build from source code and can be installed together with the library. diff --git a/doc/GSG/installation.rst b/doc/GSG/installation.rst index e6b6a09c34..d8f1933265 100644 --- a/doc/GSG/installation.rst +++ b/doc/GSG/installation.rst @@ -3,5 +3,5 @@ Installation ============ -See the `installation instructions `_ +See the `installation instructions `_ that will help you to install |short_name| successfully. \ No newline at end of file diff --git a/doc/GSG/samples.rst b/doc/GSG/samples.rst index f19a256238..18bf812801 100644 --- a/doc/GSG/samples.rst +++ b/doc/GSG/samples.rst @@ -10,40 +10,40 @@ The following samples are available: * **Containers** - * `concurrent_hash_map `_ - * `concurrent_priority_queue `_ - -* `Flow Graph `_ - * `A solution to the binpacking problem using a queue_node, a buffer_node, and function_node. `_ - * `Cholesky Factorization algorithm `_ - * `An implementation of dining philosophers in graph using the reserving join_node `_ - * `A parallel implementation of bzip2 block-sorting file compressor `_ - * `An example of a collection of digital logic gates that can be easily composed into larger circuits `_ - * `An example of a Kohonen Self-Organizing Map using cancellation `_ + * `concurrent_hash_map `_ + * `concurrent_priority_queue `_ + +* `Flow Graph `_ + * `A solution to the binpacking problem using a queue_node, a buffer_node, and function_node. `_ + * `Cholesky Factorization algorithm `_ + * `An implementation of dining philosophers in graph using the reserving join_node `_ + * `A parallel implementation of bzip2 block-sorting file compressor `_ + * `An example of a collection of digital logic gates that can be easily composed into larger circuits `_ + * `An example of a Kohonen Self-Organizing Map using cancellation `_ * `Split computational kernel for execution between CPU and GPU `_ * **Algorithms** - * `parallel_for `_ - * `Game of life overlay `_ - * `Polygon overlay `_ - * `Parallel seismic wave simulation `_ - * `Parallel 2-D raytracer/renderer `_ - * `Find largest matching substrings `_ + * `parallel_for `_ + * `Game of life overlay `_ + * `Polygon overlay `_ + * `Parallel seismic wave simulation `_ + * `Parallel 2-D raytracer/renderer `_ + * `Find largest matching substrings `_ * `Resumable task: Split computational kernel for execution between CPU and GPU `_ - * `parallel_for_each `_ - * `parallel_pipeline `_ - * `parallel_reduce `_ + * `parallel_for_each `_ + * `parallel_pipeline `_ + * `parallel_reduce `_ * **Task Scheduler** - * `task_arena `_ - * `task_group `_ + * `task_arena `_ + * `task_group `_ * `Execute similar computational kernels, with one task executing the SYCL* code and the other task executing the oneTBB code `_ * **Other** - * `Compute Fibonacci numbers in different ways `_ + * `Compute Fibonacci numbers in different ways `_ .. note:: You can also refer to the `oneAPI Samples `_ to learn more about the ecosystem. \ No newline at end of file diff --git a/doc/GSG/system_requirements.rst b/doc/GSG/system_requirements.rst index d5e951f35a..593680147f 100644 --- a/doc/GSG/system_requirements.rst +++ b/doc/GSG/system_requirements.rst @@ -3,4 +3,4 @@ System Requirements ******************* -Refer to the `oneTBB System Requirements `_. \ No newline at end of file +Refer to the `oneTBB System Requirements `_. \ No newline at end of file diff --git a/doc/README.md b/doc/README.md index 0cdd56f9c1..0e17d2e2dd 100644 --- a/doc/README.md +++ b/doc/README.md @@ -15,7 +15,7 @@ Do the following to generate HTML output of the documentation: 1. Clone oneTBB repository: ``` -git clone https://github.com/oneapi-src/oneTBB.git +git clone https://github.com/uxlfoundation/oneTBB.git ``` 2. Go to the `doc` folder: diff --git a/doc/conf.py b/doc/conf.py index 00dfed0e7f..a16dd5dec9 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -126,13 +126,13 @@ if BUILD_TYPE == 'dita': html_theme_options = { - 'repository_url': 'https://github.com/oneapi-src/oneTBB', + 'repository_url': 'https://github.com/uxlfoundation/oneTBB', 'path_to_docs': 'doc', 'repository_branch': 'master' } else: html_theme_options = { - 'repository_url': 'https://github.com/oneapi-src/oneTBB', + 'repository_url': 'https://github.com/uxlfoundation/oneTBB', 'path_to_docs': 'doc', 'use_issues_button': True, 'use_edit_page_button': True, @@ -140,7 +140,7 @@ } if BUILD_TYPE != 'oneapi' and BUILD_TYPE != 'dita': - html_theme_options["extra_footer"]="
© Intel Corporation. Intel, the Intel logo, and other Intel marks are trademarks of Intel Corporation or its subsidiaries. Other names and brands may be claimed as the property of others. No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document, with the sole exception that code included in this document is licensed subject to the Zero-Clause BSD open source license (OBSD), http://opensource.org/licenses/0BSD.

oneTBB is licensed under Apache License Version 2.0. Refer to the LICENSE file for the full license text and copyright notice.
" + html_theme_options["extra_footer"]="
© Intel Corporation. Intel, the Intel logo, and other Intel marks are trademarks of Intel Corporation or its subsidiaries. Other names and brands may be claimed as the property of others. No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document, with the sole exception that code included in this document is licensed subject to the Zero-Clause BSD open source license (OBSD), http://opensource.org/licenses/0BSD.

oneTBB is licensed under Apache License Version 2.0. Refer to the LICENSE file for the full license text and copyright notice.
" # Add any paths that contain custom static files (such as style sheets) here, diff --git a/doc/main/intro/help_support.rst b/doc/main/intro/help_support.rst index 278083d658..a1ab4097bc 100644 --- a/doc/main/intro/help_support.rst +++ b/doc/main/intro/help_support.rst @@ -12,4 +12,4 @@ Getting Help and Support For general information about oneTBB technical support, product updates, user forums, FAQs, tips and tricks and other support - questions, go to `GitHub issues `_. + questions, go to `GitHub issues `_. diff --git a/doc/main/tbb_userguide/std_invoke.rst b/doc/main/tbb_userguide/std_invoke.rst index 17ee7add99..d536eae8b2 100644 --- a/doc/main/tbb_userguide/std_invoke.rst +++ b/doc/main/tbb_userguide/std_invoke.rst @@ -204,14 +204,14 @@ Find More The following APIs supports Callable object as Bodies: -* `parallel_for `_ -* `parallel_reduce `_ -* `parallel_deterministic_reduce `_ -* `parallel_for_each `_ -* `parallel_scan `_ -* `parallel_pipeline `_ -* `function_node `_ -* `multifunction_node `_ -* `async_node `_ -* `sequencer_node `_ -* `join_node with key_matching policy `_ +* `parallel_for `_ +* `parallel_reduce `_ +* `parallel_deterministic_reduce `_ +* `parallel_for_each `_ +* `parallel_scan `_ +* `parallel_pipeline `_ +* `function_node `_ +* `multifunction_node `_ +* `async_node `_ +* `sequencer_node `_ +* `join_node with key_matching policy `_ diff --git a/examples/README.md b/examples/README.md index 037ca4d4e3..bf38ffba24 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ This directory contains example usages of oneAPI Threading Building Blocks. | Code sample name | Description |:--- |:--- -| getting_started/sub_string_finder | Example referenced by the [oneAPI Threading Building Blocks Get Started Guide](https://oneapi-src.github.io/oneTBB/GSG/get_started.html#get-started-guide). Finds the largest matching substrings. +| getting_started/sub_string_finder | Example referenced by the [oneAPI Threading Building Blocks Get Started Guide](https://uxlfoundation.github.io/oneTBB/GSG/get_started.html#get-started-guide). Finds the largest matching substrings. | concurrent_hash_map/count_strings | Concurrently inserts strings into a `concurrent_hash_map` container. | concurrent_priority_queue/shortpath | Solves the single source shortest path problem using a `concurrent_priority_queue` container. | graph/binpack | A solution to the binpacking problem using a `queue_node`, a `buffer_node`, and `function_node`s. @@ -26,7 +26,7 @@ This directory contains example usages of oneAPI Threading Building Blocks. | test_all/fibonacci | Compute Fibonacci numbers in different ways. ## System Requirements -Refer to the [System Requirements](https://github.com/oneapi-src/oneTBB/blob/master/SYSTEM_REQUIREMENTS.md) for the list of supported hardware and software. +Refer to the [System Requirements](https://github.com/uxlfoundation/oneTBB/blob/master/SYSTEM_REQUIREMENTS.md) for the list of supported hardware and software. ### Graphical User Interface (GUI) Some examples (e.g., fractal, seismic, tachyon, polygon_overlay) support different GUI modes, which may be defined via the `EXAMPLES_UI_MODE` CMake variable. diff --git a/integration/pkg-config/tbb.pc.in b/integration/pkg-config/tbb.pc.in index 34ea3bea17..2fe03c72e9 100644 --- a/integration/pkg-config/tbb.pc.in +++ b/integration/pkg-config/tbb.pc.in @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023 Intel Corporation +# Copyright (c) 2021-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. @@ -18,7 +18,7 @@ includedir=@_includedir_for_pc_file@ Name: oneAPI Threading Building Blocks (oneTBB) Description: C++ library for parallel programming on multi-core processors. -URL: https://github.com/oneapi-src/oneTBB +URL: https://github.com/uxlfoundation/oneTBB Version: @TBB_VERSION@ Libs: -L${libdir} @_tbb_pc_extra_libdir@ -l@_tbb_pc_lib_name@ Cflags: -I${includedir} From bd689b5a7e8829786a47890cb54a117e3b45970a Mon Sep 17 00:00:00 2001 From: Kursat Aktas Date: Thu, 5 Dec 2024 22:56:48 +0300 Subject: [PATCH 26/37] Introducing oneTBB Guru on Gurubase.io (#1566) Signed-off-by: Kursat Aktas Co-authored-by: Mike Voss --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36bdd6ce26..d1c04dd864 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Join the community on GitHub Discussions](https://badgen.net/badge/join%20the%20discussion/on%20github/blue?icon=github)](https://github.com/uxlfoundation/oneTBB/discussions) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9125/badge)](https://www.bestpractices.dev/projects/9125) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/uxlfoundation/oneTBB/badge)](https://securityscorecards.dev/viewer/?uri=github.com/uxlfoundation/oneTBB) +[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20oneTBB%20Guru-006BFF)](https://gurubase.io/g/onetbb) oneTBB is a flexible C++ library that simplifies the work of adding parallelism to complex applications, even if you are not a threading expert. From 9b843adfbbdc11d4da02dc50d681e3ec3b382713 Mon Sep 17 00:00:00 2001 From: Olga Malysheva Date: Mon, 9 Dec 2024 12:59:28 +0100 Subject: [PATCH 27/37] Replace deprecated macos-12 environment with macos-14 and macos-15 (#1578) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7ccfa2590..abda1e140f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -198,7 +198,7 @@ jobs: fail-fast: false matrix: include: - - os: macos-12 + - os: macos-14 c_compiler: clang cxx_compiler: clang++ std: 14 @@ -314,7 +314,7 @@ jobs: fail-fast: false matrix: include: - - os: macos-12 + - os: macos-15 c_compiler: clang cxx_compiler: clang++ std: 14 From dd27a25bdfd90d085520d650a4a437c2ac00ec59 Mon Sep 17 00:00:00 2001 From: Pavel Kumbrasev Date: Mon, 9 Dec 2024 16:38:03 +0000 Subject: [PATCH 28/37] [RFC] Propose parallel phase API extension for task_arena to warm-up/retain/release worker threads (#1522) Signed-off-by: pavelkumbrasev Signed-off-by: Isaev, Ilya Co-authored-by: Aleksei Fedotov Co-authored-by: Ilya Isaev Co-authored-by: Alexey Kukanov Co-authored-by: Mike Voss --- .../parallel_block_for_task_arena/README.md | 274 ++++++++++++++++++ .../alternative_proposal.png | Bin 0 -> 111506 bytes .../completely_disable_new_behavior.png | Bin 0 -> 171804 bytes .../parallel_phase_introduction.png | Bin 0 -> 99675 bytes .../parallel_phase_state_final.png | Bin 0 -> 219191 bytes .../parallel_phase_state_initial.png | Bin 0 -> 169595 bytes 6 files changed, 274 insertions(+) create mode 100644 rfcs/proposed/parallel_block_for_task_arena/README.md create mode 100644 rfcs/proposed/parallel_block_for_task_arena/alternative_proposal.png create mode 100644 rfcs/proposed/parallel_block_for_task_arena/completely_disable_new_behavior.png create mode 100644 rfcs/proposed/parallel_block_for_task_arena/parallel_phase_introduction.png create mode 100644 rfcs/proposed/parallel_block_for_task_arena/parallel_phase_state_final.png create mode 100644 rfcs/proposed/parallel_block_for_task_arena/parallel_phase_state_initial.png diff --git a/rfcs/proposed/parallel_block_for_task_arena/README.md b/rfcs/proposed/parallel_block_for_task_arena/README.md new file mode 100644 index 0000000000..207c318b4d --- /dev/null +++ b/rfcs/proposed/parallel_block_for_task_arena/README.md @@ -0,0 +1,274 @@ +# Adding API for parallel phase to task_arena to warm-up/retain/release worker threads + +## Introduction + +In oneTBB, there has never been an API that allows users to block worker threads within the arena. +This design choice was made to preserve the composability of the application. +Before PR#1352, workers moved to the thread pool to sleep once there were no arenas with active +demand. However, PR#1352 introduced a delayed leave behavior to the library that +results in blocking threads for an _implementation-defined_ duration inside an arena +if there is no active demand arcoss all arenas. This change significantly +improved performance for various applications on high thread count systems. +The main idea is that usually, after one parallel computation ends, +another will start after some time. The delayed leave behavior is a heuristic to utilize this, +covering most cases within _implementation-defined_ duration. + +However, the new behavior is not the perfect match for all the scenarios: +* The heuristic of delayed leave is unsuitable for the tasks that are submitted + in an unpredictable pattern and/or durations. +* If oneTBB is used in composable scenarios it is not behaving as + a good citizen consuming CPU resources. + * For example, if an application runs a series of stages where oneTBB is used for one stage + and OpenMP is used for a subsequent stage, there is a chance that oneTBB workers will + interfere with OpenMP threads. This interference might result in slight oversubscription, + which in turn might lead to underperformance. + +So there are two related problems but with different resolutions: +* Completely disable new behavior for scenarios where the heuristic of delayed leave is unsuitable. +* Optimize library behavior so customers can benefit from the heuristic of delayed leave but + make it possible to indicate that "it is the time for the TBB arena to release threads". + +## Proposal + +Let's tackle these problems one by one. + +### Completely disable new behavior + +Let’s consider both “Delayed leave” and “Fast leave” as 2 different states in state machine.
+* The "Delayed leave" heuristic benefits most of the workloads. Therefore, this is the + default behavior for arena. +* Workloads that has rather negative performance impact from the heuristic of delayed leave + can create an arena in “Fast leave” state. + + + +There will be a question that we need to answer: +* Do we see any value if arena potentially can transition from one to another state? + +To answer this question, the following scenarios should be considered: +* What if different types of workloads are mixed in one application? +* Different types of arenas can be used for different types of workloads. + +### When threads should leave? + +oneTBB itself can only guess when the ideal time to release threads from the arena is. +Therefore, it does its best effort to preserve and enhance performance without completely +messing up composability guarantees (that is how delayed leave is implemented). + +As we already discussed, there are cases where it does not work perfectly, +therefore customers that want to further optimize this +aspect of oneTBB behavior should be able to do it. + +This problem can be considered from another angle. Essentially, if the user can indicate +where parallel computation ends, they can also indicate where it starts. + + + +With this approach, the user not only releases threads when necessary but also specifies a +programmable block where worker threads should expect new work coming regularly +to the executing arena. + +Let’s add a new state to the existing state machine. To represent "Parallel Phase" state. + +> **_NOTE:_** The "Fast leave" state is colored Grey just for simplicity of the chart. + Let's assume that arena was created with the "Delayed leave". + The logic demonstrated below is applicable to the "Fast leave" as well. + + + +This state diagram leads to several questions: +* What if there are multiple Parallel Phases? +* If “End of Parallel Phase” leads back to “Delayed leave” how soon will threads + be released from arena? + * What if we indicated that threads should leave arena after the "Parallel Phase"? + * What if we just indicated the end of the "Parallel Phase"? + +The extended state machine aims to answer these questions. +* The first call to the “Start of Phase” will transition into the “Parallel Phase” state. +* The last call to the “End of Phase” will transition back to the “Delayed leave” state + or into the "One-time Fast leave" if it is indicated that threads should leave sooner. +* Concurrent or nested calls to the “Start of Phase” or the “End of Phase” + increment/decrement a reference counter. + + + +Let's consider the semantics that an API for explicit parallel phases can provide: +* Start of a parallel phase: + * Indicates the point from which the scheduler can use a hint and keep threads in the arena + for longer. + * Serves as a warm-up hint to the scheduler: + * Allows reducing delays of computation start by initiating the wake-up of worker threads + in advance. +* "Parallel phase" itself: + * Scheduler can implement different policies to retain threads in the arena. + * For instance, more aggressive policy might be implemented for _parallel phase_. + It can be beneficial in cases when the default arena leave policy is not sufficient enough. + * The semantics for retaining threads is a hint to the scheduler; + thus, no real guarantee is provided. The scheduler can ignore the hint and + move threads to another arena or to sleep if conditions are met. +* End of a parallel phase: + * Indicates the point from which the scheduler may drop the hint and + no longer retain threads in the arena. + * Indicates that worker threads should avoid busy-waiting once there is no more work in the arena. + * Temporarily overrides the default arena leave policy, which will be restored when + new work is submitted. + + +### Proposed API + +Summary of API changes: + +* Add enumeration class for the arena leave policy. +* Add the policy as the last parameter to the arena constructor and initializer +defaulted to "automatic". +* Add functions to start and end the parallel phase to the `task_arena` class +and the `this_task_arena` namespace. +* Add RAII class to map a parallel phase to a code scope. + +```cpp +class task_arena { + enum class leave_policy : /* unspecified type */ { + automatic = /* unspecifed */, + fast = /* unspecifed */, + }; + + task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1, + priority a_priority = priority::normal, + leave_policy a_leave_policy = leave_policy::automatic); + + task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, + priority a_priority = priority::normal, + leave_policy a_leave_policy = leave_policy::automatic); + + void initialize(int max_concurrency, unsigned reserved_for_masters = 1, + priority a_priority = priority::normal, + leave_policy a_leave_policy = leave_policy::automatic); + + void initialize(constraints a_constraints, unsigned reserved_for_masters = 1, + priority a_priority = priority::normal, + leave_policy a_leave_policy = leave_policy::automatic); + + void start_parallel_phase(); + void end_parallel_phase(bool with_fast_leave = false); + + class scoped_parallel_phase { + scoped_parallel_phase(task_arena& ta, bool with_fast_leave = false); + }; +}; + +namespace this_task_arena { + void start_parallel_phase(); + void end_parallel_phase(bool with_fast_leave = false); +} +``` +The _parallel phase_ continues until each previous `start_parallel_phase` call +to the same arena has a matching `end_parallel_phase` call.
+Let's introduce RAII scoped object that will help to manage the contract. + +If the end of the parallel phase is not indicated by the user, it will be done automatically when +the last public reference is removed from the arena (i.e., task_arena has been destroyed or, +for an implicitly created arena, the thread that owns it has completed). +This ensures correctness is preserved (threads will not be retained forever). + +### Examples + +Following code snippets show how the new API can be used. + +```cpp +void task_arena_leave_policy_example() { + tbb::task_arena ta{tbb::task_arena::automatic, 1, priority::normal, leave_policy::fast}; + ta.execute([]() { + // Parallel computation + }); + // Different parallel runtime is used + // so it is preferred that worker threads won't be retained + // in the arena at this point. + #pragma omp parallel for + for (int i = 0; i < work_size; ++i) { + // Computation + } +} + +void parallel_phase_example() { + tbb::this_task_arena::start_parallel_phase(); + tbb::parallel_for(0, work_size, [] (int idx) { + // User defined body + }); + + // Some serial computation + + tbb::parallel_for(0, work_size, [] (int idx) { + // User defined body + }); + tbb::this_task_arena::end_parallel_phase(/*with_fast_leave=*/true); + + // Different parallel runtime (for example, OpenMP) is used + // so it is preferred that worker threads won't be retained + // in the arena at this point. + #pragma omp parallel for + for (int i = 0; i < work_size; ++i) { + // Computation + } +} + +void scoped_parallel_phase_example() { + tbb::task_arena ta{/*arena constraints*/}; + { + // Start of the parallel phase + tbb::task_arena::scoped_parallel_phase phase{ta, /*with_fast_leave=*/true}; + ta.execute([]() { + // Parallel computation + }); + + // Serial computation + + ta.execute([]() { + // Parallel computation + }); + } // End of the parallel phase + + // Different parallel runtime (for example, OpenMP) is used + // so it is preferred that worker threads won't be retained + // in the arena at this point. + #pragma omp parallel for + for (int i = 0; i < work_size; ++i) { + // Computation + } +} + +``` + +## Considerations + +The alternative approaches were also considered.
+We can express this state machine as complete graph and provide low-level interface that +will give control over state transition. + + + +We considered this approach too low-level. Plus, it leaves a question: "How to manage concurrent changes of the state?". + +The retaining of worker threads should be implemented with care because +it might introduce performance problems if: +* Threads cannot migrate to another arena because they are + retained in the current arena. +* Compute resources are not homogeneous, e.g., the CPU is hybrid. + Heavier involvement of less performant core types might result in artificial work + imbalance in the arena. + + +## Open Questions in Design + +Some open questions that remain: +* Are the suggested APIs sufficient? + * In the current version of proposed API, the `scoped_parallel_phase` object can be created + only for already existing `task_arena`. Should it be possible for `this_task_arena` as well? + * What should be expected from "Parallel Phase" API for `this_task_arena` when a calling thread + doesn't yet have any associated arena? + * Should parallel phase API be limited only to RAII-only style? + * Are there any scenarios where inconvenience of handling `scoped_parallel_phase` object is + not acceptable? +* Are there additional use cases that should be considered that we missed in our analysis? +* 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? diff --git a/rfcs/proposed/parallel_block_for_task_arena/alternative_proposal.png b/rfcs/proposed/parallel_block_for_task_arena/alternative_proposal.png new file mode 100644 index 0000000000000000000000000000000000000000..654fa39fbfae2bec1ecd3c7cbe9938754e614461 GIT binary patch literal 111506 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 literal 0 HcmV?d00001 diff --git a/rfcs/proposed/parallel_block_for_task_arena/completely_disable_new_behavior.png b/rfcs/proposed/parallel_block_for_task_arena/completely_disable_new_behavior.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b6fc2bdae0c773531a818a8e0484cc60d114ee GIT binary patch literal 171804 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 c? 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