diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -287,6 +287,12 @@ "Build libc++ with an externalized threading library. This option may only be set to ON when LIBCXX_ENABLE_THREADS=ON" OFF) +if (LIBCXX_ENABLE_THREADS) + set(LIBCXX_PSTL_CPU_BACKEND "std_thread" CACHE STRING "Which PSTL CPU backend to use") +else() + set(LIBCXX_PSTL_CPU_BACKEND "serial" CACHE STRING "Which PSTL CPU backend to use") +endif() + # Misc options ---------------------------------------------------------------- # FIXME: Turn -pedantic back ON. It is currently off because it warns # about #include_next which is used everywhere. @@ -804,6 +810,15 @@ config_define(0 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT) endif() +if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "serial") + config_define(1 _LIBCPP_PSTL_CPU_BACKEND_SERIAL) +elseif(LIBCXX_PSTL_CPU_BACKEND STREQUAL "std_thread") + config_define(1 _LIBCPP_PSTL_CPU_BACKEND_THREAD) +else() + message(FATAL_ERROR "LIBCXX_PSTL_CPU_BACKEND is set to ${LIBCXX_PSTL_CPU_BACKEND}, which is not a valid backend. + Valid backends are: serial, std_thread") +endif() + if (LIBCXX_ABI_DEFINES) set(abi_defines) foreach (abi_define ${LIBCXX_ABI_DEFINES}) diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -78,6 +78,7 @@ __algorithm/pstl_backends/cpu_backends/find_if.h __algorithm/pstl_backends/cpu_backends/for_each.h __algorithm/pstl_backends/cpu_backends/serial.h + __algorithm/pstl_backends/cpu_backends/thread.h __algorithm/pstl_backends/cpu_backends/transform.h __algorithm/pstl_fill.h __algorithm/pstl_find.h diff --git a/libcxx/include/__algorithm/pstl_backend.h b/libcxx/include/__algorithm/pstl_backend.h --- a/libcxx/include/__algorithm/pstl_backend.h +++ b/libcxx/include/__algorithm/pstl_backend.h @@ -85,7 +85,7 @@ }; # endif -# if defined(_PSTL_CPU_BACKEND_SERIAL) +# if defined(_LIBCPP_PSTL_CPU_BACKEND_SERIAL) || defined(_LIBCPP_PSTL_CPU_BACKEND_THREAD) template <> struct __select_backend { using type = __cpu_backend_tag; diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h @@ -11,8 +11,10 @@ #include <__config> -#if defined(_LIBCPP_HAS_NO_THREADS) || defined(_PSTL_CPU_BACKEND_SERIAL) +#if defined(_LIBCPP_PSTL_CPU_BACKEND_SERIAL) # include <__algorithm/pstl_backends/cpu_backends/serial.h> +#elif defined(_LIBCPP_PSTL_CPU_BACKEND_THREAD) +# include <__algorithm/pstl_backends/cpu_backends/thread.h> #else # error "Invalid CPU backend choice" #endif diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_THREAD_H +#define _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_THREAD_H + +#include <__assert> +#include <__config> +#include <__functional/function.h> +#include <__iterator/iterator_traits.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace __par_backend { +inline namespace __thread_cpu_backend { + +_LIBCPP_FUNC_VIS unsigned __hardware_concurrency(); +_LIBCPP_FUNC_VIS void* __make_thread_pool(unsigned __expected_thread_count); +_LIBCPP_FUNC_VIS void __add_thread(void* __thread_pool, function); +_LIBCPP_FUNC_VIS void __join_threads_and_destroy_pool(void* __thread_pool); + +template +_LIBCPP_HIDE_FROM_ABI void __parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Fp __f) { + using __diff_t = __iter_diff_t<_RandomAccessIterator>; + + auto __thread_count = [&]() { + auto __max_thread_count = __hardware_concurrency(); + return __max_thread_count == 0 ? 1 : __max_thread_count; + }(); + + void* __thread_pool = __thread_cpu_backend::__make_thread_pool(__thread_count); + + const auto __brick_size = ((__last - __first) / static_cast<__diff_t>(__thread_count)) + 1; + + for (size_t __i = 0; __i != __thread_count; ++__i) { + if (__last - __first >= __brick_size) { + __thread_cpu_backend::__add_thread( + __thread_pool, [&__f, __brick_first = __first, __brick_last = __first + __brick_size]() { + __f(__brick_first, __brick_last); + }); + __first += __brick_size; + } else { + __thread_cpu_backend::__add_thread(__thread_pool, [&__f, __brick_first = __first, __brick_last = __last]() { + __f(__brick_first, __brick_last); + }); + __first = __last; + } + } + _LIBCPP_ASSERT(__first == __last, "__parallel_for didn't iterator over all elements!"); + + __thread_cpu_backend::__join_threads_and_destroy_pool(__thread_pool); +} + +_LIBCPP_HIDE_FROM_ABI inline void __cancel_execution() {} + +} // namespace __thread_cpu_backend +} // namespace __par_backend + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && && _LIBCPP_STD_VER >= 17 + +#endif // _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_THREAD_H diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1273,7 +1273,6 @@ // TODO: Make this a proper configuration option #define _PSTL_PAR_BACKEND_SERIAL -#define _PSTL_CPU_BACKEND_SERIAL #define _PSTL_PRAGMA(x) _Pragma(# x) diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -32,6 +32,10 @@ #cmakedefine01 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT #cmakedefine _LIBCPP_ENABLE_DEBUG_MODE +// PSTL backends +#cmakedefine _LIBCPP_PSTL_CPU_BACKEND_SERIAL +#cmakedefine _LIBCPP_PSTL_CPU_BACKEND_THREAD + // __USE_MINGW_ANSI_STDIO gets redefined on MinGW #ifdef __clang__ # pragma clang diagnostic push diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -312,6 +312,7 @@ set(LIBCXX_EXPERIMENTAL_SOURCES experimental/memory_resource.cpp format.cpp + pstl/thread_backend.cpp ) add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES}) diff --git a/libcxx/src/pstl/thread_backend.cpp b/libcxx/src/pstl/thread_backend.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/pstl/thread_backend.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__algorithm/pstl_backends/cpu_backends/thread.h> +#include +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace __par_backend::inline __thread_cpu_backend { + +unsigned __hardware_concurrency() { return thread::hardware_concurrency(); } + +void* __make_thread_pool(unsigned expected_thread_count) { + auto pool = new std::vector; + pool->reserve(expected_thread_count); + return pool; +} + +void __add_thread(void* thread_pool, function run) { + static_cast*>(thread_pool)->emplace_back(run); +} + +void __join_threads_and_destroy_pool(void* thread_pool) { + auto& pool = *static_cast*>(thread_pool); + for (auto& thread : pool) + thread.join(); + delete &pool; +} + +// NOLINTNEXTLINE(llvm-namespace-comment) // TODO: fix discrepancy between clang-format and clang-tidy +} // namespace __par_backend::inline __thread_cpu_backend + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -121,6 +121,7 @@ #include <__algorithm/pstl_backends/cpu_backends/find_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/find_if.h'}} #include <__algorithm/pstl_backends/cpu_backends/for_each.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/for_each.h'}} #include <__algorithm/pstl_backends/cpu_backends/serial.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/serial.h'}} +#include <__algorithm/pstl_backends/cpu_backends/thread.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/thread.h'}} #include <__algorithm/pstl_backends/cpu_backends/transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/transform.h'}} #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}} #include <__algorithm/ranges_adjacent_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_adjacent_find.h'}} diff --git a/libcxx/utils/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt --- a/libcxx/utils/data/ignore_format.txt +++ b/libcxx/utils/data/ignore_format.txt @@ -814,6 +814,7 @@ libcxx/src/mutex_destructor.cpp libcxx/src/new.cpp libcxx/src/optional.cpp +libcxx/src/pstl/thread_backend.cpp libcxx/src/random.cpp libcxx/src/random_shuffle.cpp libcxx/src/regex.cpp