From 061261c4956378f7437df134f48048fc27059381 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 3 Nov 2025 22:26:32 +0100 Subject: [PATCH 1/6] update OpenMP linking and test environment setup --- cmake/openmp.cmake | 20 ++++++- modules/util/include/func_test_util.hpp | 2 + modules/util/include/perf_test_util.hpp | 2 + modules/util/include/util.hpp | 70 +++++++++++++++++++++++-- scripts/run_tests.py | 27 ++++++++-- 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/cmake/openmp.cmake b/cmake/openmp.cmake index a254a88ff..e4edc496a 100644 --- a/cmake/openmp.cmake +++ b/cmake/openmp.cmake @@ -20,6 +20,22 @@ endfunction() function(ppc_link_openmp exec_func_lib) find_package(OpenMP REQUIRED) - target_link_libraries(${exec_func_lib} PUBLIC ${OpenMP_libomp_LIBRARY} - OpenMP::OpenMP_CXX) + # Link the canonical imported target if available + if (TARGET OpenMP::OpenMP_CXX) + target_link_libraries(${exec_func_lib} PUBLIC OpenMP::OpenMP_CXX) + endif() + + if (APPLE) + # Homebrew libomp common paths + find_path(LIBOMP_INCLUDE_DIR omp.h HINTS /opt/homebrew/opt/libomp/include /usr/local/opt/libomp/include) + find_library(LIBOMP_LIBRARY omp HINTS /opt/homebrew/opt/libomp/lib /usr/local/opt/libomp/lib) + if (LIBOMP_INCLUDE_DIR) + target_include_directories(${exec_func_lib} PUBLIC ${LIBOMP_INCLUDE_DIR}) + endif() + if (LIBOMP_LIBRARY) + target_link_libraries(${exec_func_lib} PUBLIC ${LIBOMP_LIBRARY}) + # Ensure Clang generates OpenMP code + target_compile_options(${exec_func_lib} PUBLIC -Xclang -fopenmp) + endif() + endif() endfunction() diff --git a/modules/util/include/func_test_util.hpp b/modules/util/include/func_test_util.hpp index 9a4e71327..6cae30288 100644 --- a/modules/util/include/func_test_util.hpp +++ b/modules/util/include/func_test_util.hpp @@ -61,6 +61,8 @@ class BaseRunFuncTests : public ::testing::TestWithParam #include +#include #include #include +#include #include +#include #include +#include +#include #include #ifdef __GNUG__ # include @@ -17,6 +23,9 @@ # pragma warning(disable : 4459) #endif +#include + +#include #include /// @brief JSON namespace used for settings and config parsing. @@ -55,7 +64,7 @@ class DestructorFailureFlag { enum class GTestParamIndex : uint8_t { kTaskGetter, kNameTest, kTestParams }; -std::string GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path); +std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path); int GetNumThreads(); int GetNumProc(); double GetTaskMaxTime(); @@ -66,13 +75,13 @@ std::string GetNamespace() { std::string name = typeid(T).name(); #ifdef __GNUC__ int status = 0; - std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), - std::free}; + std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free}; name = (status == 0) ? demangled.get() : name; #endif #ifdef _MSC_VER const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; - for (const auto &prefix : prefixes) { + for (const auto& prefix : prefixes) { if (name.starts_with(prefix)) { name = name.substr(prefix.size()); break; @@ -90,4 +99,57 @@ inline std::shared_ptr InitJSONPtr() { bool IsUnderMpirun(); +namespace test { + +[[nodiscard]] inline std::string SanitizeToken(std::string_view token_sv) { + std::string token{token_sv}; + auto is_allowed = [](char c) { + return std::isalnum(static_cast(c)) || c == '_' || c == '-' || c == '.'; + }; + std::ranges::replace(token, ' ', '_'); + for (char& ch : token) { + if (!is_allowed(ch)) { + ch = '_'; + } + } + return token; +} + +class ScopedPerTestEnv { + public: + explicit ScopedPerTestEnv(const std::string& token) + : set_uid_("PPC_TEST_UID", token), set_tmp_("PPC_TEST_TMPDIR", CreateTmpDir(token)) {} + + private: + static std::string CreateTmpDir(const std::string& token) { + namespace fs = std::filesystem; + const fs::path tmp = fs::temp_directory_path() / (std::string("ppc_test_") + token); + std::error_code ec; + fs::create_directories(tmp, ec); + (void)ec; + return tmp.string(); + } + + env::detail::set_scoped_environment_variable set_uid_; + env::detail::set_scoped_environment_variable set_tmp_; +}; + +[[nodiscard]] inline std::string MakeCurrentGTestToken(std::string_view fallback_name) { + const auto* unit = ::testing::UnitTest::GetInstance(); + const auto* info = (unit != nullptr) ? unit->current_test_info() : nullptr; + std::ostringstream os; + if (info != nullptr) { + os << info->test_suite_name() << "." << info->name(); + } else { + os << fallback_name; + } + return SanitizeToken(os.str()); +} + +inline ScopedPerTestEnv MakePerTestEnvForCurrentGTest(std::string_view fallback_name) { + return ScopedPerTestEnv(MakeCurrentGTestToken(fallback_name)); +} + +} // namespace test + } // namespace ppc::util diff --git a/scripts/run_tests.py b/scripts/run_tests.py index a6f75a75f..c14ce5b04 100755 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -135,30 +135,47 @@ def run_processes(self, additional_mpi_args): ) mpi_running = ( - [self.mpi_exec] + shlex.split(additional_mpi_args) + ["-np", ppc_num_proc] + [self.mpi_exec] + + shlex.split(additional_mpi_args) + + [ + "-x", + "PPC_NUM_THREADS", + "-x", + "OMP_NUM_THREADS", + "-np", + ppc_num_proc, + ] ) if not self.__ppc_env.get("PPC_ASAN_RUN"): for task_type in ["all", "mpi"]: self.__run_exec( mpi_running + [str(self.work_dir / "ppc_func_tests")] - + self.__get_gtest_settings(1, "_" + task_type) + + self.__get_gtest_settings(1, "_" + task_type + "_") ) def run_performance(self): if not self.__ppc_env.get("PPC_ASAN_RUN"): - mpi_running = [self.mpi_exec, "-np", self.__ppc_num_proc] + mpi_running = [ + self.mpi_exec, + "-x", + "PPC_NUM_THREADS", + "-x", + "OMP_NUM_THREADS", + "-np", + self.__ppc_num_proc, + ] for task_type in ["all", "mpi"]: self.__run_exec( mpi_running + [str(self.work_dir / "ppc_perf_tests")] - + self.__get_gtest_settings(1, "_" + task_type) + + self.__get_gtest_settings(1, "_" + task_type + "_") ) for task_type in ["omp", "seq", "stl", "tbb"]: self.__run_exec( [str(self.work_dir / "ppc_perf_tests")] - + self.__get_gtest_settings(1, "_" + task_type) + + self.__get_gtest_settings(1, "_" + task_type + "_") ) From 36cbfb0039884522184574499b29dfd804dd0199 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 3 Nov 2025 22:31:05 +0100 Subject: [PATCH 2/6] update formatting and OpenMP linking --- cmake/openmp.cmake | 14 ++++++++------ modules/util/include/util.hpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cmake/openmp.cmake b/cmake/openmp.cmake index e4edc496a..004cdd44f 100644 --- a/cmake/openmp.cmake +++ b/cmake/openmp.cmake @@ -21,18 +21,20 @@ endfunction() function(ppc_link_openmp exec_func_lib) find_package(OpenMP REQUIRED) # Link the canonical imported target if available - if (TARGET OpenMP::OpenMP_CXX) + if(TARGET OpenMP::OpenMP_CXX) target_link_libraries(${exec_func_lib} PUBLIC OpenMP::OpenMP_CXX) endif() - if (APPLE) + if(APPLE) # Homebrew libomp common paths - find_path(LIBOMP_INCLUDE_DIR omp.h HINTS /opt/homebrew/opt/libomp/include /usr/local/opt/libomp/include) - find_library(LIBOMP_LIBRARY omp HINTS /opt/homebrew/opt/libomp/lib /usr/local/opt/libomp/lib) - if (LIBOMP_INCLUDE_DIR) + find_path(LIBOMP_INCLUDE_DIR omp.h HINTS /opt/homebrew/opt/libomp/include + /usr/local/opt/libomp/include) + find_library(LIBOMP_LIBRARY omp HINTS /opt/homebrew/opt/libomp/lib + /usr/local/opt/libomp/lib) + if(LIBOMP_INCLUDE_DIR) target_include_directories(${exec_func_lib} PUBLIC ${LIBOMP_INCLUDE_DIR}) endif() - if (LIBOMP_LIBRARY) + if(LIBOMP_LIBRARY) target_link_libraries(${exec_func_lib} PUBLIC ${LIBOMP_LIBRARY}) # Ensure Clang generates OpenMP code target_compile_options(${exec_func_lib} PUBLIC -Xclang -fopenmp) diff --git a/modules/util/include/util.hpp b/modules/util/include/util.hpp index ea261fff1..6b1846cac 100644 --- a/modules/util/include/util.hpp +++ b/modules/util/include/util.hpp @@ -64,7 +64,7 @@ class DestructorFailureFlag { enum class GTestParamIndex : uint8_t { kTaskGetter, kNameTest, kTestParams }; -std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path); +std::string GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path); int GetNumThreads(); int GetNumProc(); double GetTaskMaxTime(); @@ -75,13 +75,13 @@ std::string GetNamespace() { std::string name = typeid(T).name(); #ifdef __GNUC__ int status = 0; - std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), - std::free}; + std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free}; name = (status == 0) ? demangled.get() : name; #endif #ifdef _MSC_VER const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; - for (const auto& prefix : prefixes) { + for (const auto &prefix : prefixes) { if (name.starts_with(prefix)) { name = name.substr(prefix.size()); break; @@ -107,7 +107,7 @@ namespace test { return std::isalnum(static_cast(c)) || c == '_' || c == '-' || c == '.'; }; std::ranges::replace(token, ' ', '_'); - for (char& ch : token) { + for (char &ch : token) { if (!is_allowed(ch)) { ch = '_'; } @@ -117,11 +117,11 @@ namespace test { class ScopedPerTestEnv { public: - explicit ScopedPerTestEnv(const std::string& token) + explicit ScopedPerTestEnv(const std::string &token) : set_uid_("PPC_TEST_UID", token), set_tmp_("PPC_TEST_TMPDIR", CreateTmpDir(token)) {} private: - static std::string CreateTmpDir(const std::string& token) { + static std::string CreateTmpDir(const std::string &token) { namespace fs = std::filesystem; const fs::path tmp = fs::temp_directory_path() / (std::string("ppc_test_") + token); std::error_code ec; @@ -135,8 +135,8 @@ class ScopedPerTestEnv { }; [[nodiscard]] inline std::string MakeCurrentGTestToken(std::string_view fallback_name) { - const auto* unit = ::testing::UnitTest::GetInstance(); - const auto* info = (unit != nullptr) ? unit->current_test_info() : nullptr; + const auto *unit = ::testing::UnitTest::GetInstance(); + const auto *info = (unit != nullptr) ? unit->current_test_info() : nullptr; std::ostringstream os; if (info != nullptr) { os << info->test_suite_name() << "." << info->name(); From 9814ff79ae8c75b6bf6e3eee3847105595b3fbe0 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 3 Nov 2025 22:59:20 +0100 Subject: [PATCH 3/6] simplify MPI command configuration in test scripts --- scripts/run_tests.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/scripts/run_tests.py b/scripts/run_tests.py index c14ce5b04..5d85b009b 100755 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -138,10 +138,6 @@ def run_processes(self, additional_mpi_args): [self.mpi_exec] + shlex.split(additional_mpi_args) + [ - "-x", - "PPC_NUM_THREADS", - "-x", - "OMP_NUM_THREADS", "-np", ppc_num_proc, ] @@ -156,15 +152,7 @@ def run_processes(self, additional_mpi_args): def run_performance(self): if not self.__ppc_env.get("PPC_ASAN_RUN"): - mpi_running = [ - self.mpi_exec, - "-x", - "PPC_NUM_THREADS", - "-x", - "OMP_NUM_THREADS", - "-np", - self.__ppc_num_proc, - ] + mpi_running = [self.mpi_exec, "-np", self.__ppc_num_proc] for task_type in ["all", "mpi"]: self.__run_exec( mpi_running From 461bf7cda343629be481848cb29bb5c9ea6329e7 Mon Sep 17 00:00:00 2001 From: Nesterov Alexander Date: Mon, 3 Nov 2025 23:00:39 +0100 Subject: [PATCH 4/6] Refactor mpi_running initialization for clarity --- scripts/run_tests.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/run_tests.py b/scripts/run_tests.py index 5d85b009b..ea2ea3bb1 100755 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -135,12 +135,7 @@ def run_processes(self, additional_mpi_args): ) mpi_running = ( - [self.mpi_exec] - + shlex.split(additional_mpi_args) - + [ - "-np", - ppc_num_proc, - ] + [self.mpi_exec] + shlex.split(additional_mpi_args) + ["-np", ppc_num_proc] ) if not self.__ppc_env.get("PPC_ASAN_RUN"): for task_type in ["all", "mpi"]: From dead833c35c204eae2eaeae41a7f0a7742acdd4b Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Tue, 4 Nov 2025 06:31:36 +0100 Subject: [PATCH 5/6] Update cmake/openmp.cmake --- cmake/openmp.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmake/openmp.cmake b/cmake/openmp.cmake index 004cdd44f..ac928297d 100644 --- a/cmake/openmp.cmake +++ b/cmake/openmp.cmake @@ -20,10 +20,8 @@ endfunction() function(ppc_link_openmp exec_func_lib) find_package(OpenMP REQUIRED) - # Link the canonical imported target if available - if(TARGET OpenMP::OpenMP_CXX) - target_link_libraries(${exec_func_lib} PUBLIC OpenMP::OpenMP_CXX) - endif() + target_link_libraries(${exec_func_lib} PUBLIC ${OpenMP_libomp_LIBRARY} + OpenMP::OpenMP_CXX) if(APPLE) # Homebrew libomp common paths From 9e634300c2cec927b1c573033c43626b83642bf4 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Tue, 4 Nov 2025 06:31:43 +0100 Subject: [PATCH 6/6] Update cmake/openmp.cmake --- cmake/openmp.cmake | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/cmake/openmp.cmake b/cmake/openmp.cmake index ac928297d..a254a88ff 100644 --- a/cmake/openmp.cmake +++ b/cmake/openmp.cmake @@ -22,20 +22,4 @@ function(ppc_link_openmp exec_func_lib) find_package(OpenMP REQUIRED) target_link_libraries(${exec_func_lib} PUBLIC ${OpenMP_libomp_LIBRARY} OpenMP::OpenMP_CXX) - - if(APPLE) - # Homebrew libomp common paths - find_path(LIBOMP_INCLUDE_DIR omp.h HINTS /opt/homebrew/opt/libomp/include - /usr/local/opt/libomp/include) - find_library(LIBOMP_LIBRARY omp HINTS /opt/homebrew/opt/libomp/lib - /usr/local/opt/libomp/lib) - if(LIBOMP_INCLUDE_DIR) - target_include_directories(${exec_func_lib} PUBLIC ${LIBOMP_INCLUDE_DIR}) - endif() - if(LIBOMP_LIBRARY) - target_link_libraries(${exec_func_lib} PUBLIC ${LIBOMP_LIBRARY}) - # Ensure Clang generates OpenMP code - target_compile_options(${exec_func_lib} PUBLIC -Xclang -fopenmp) - endif() - endif() endfunction()