diff --git a/.github/actions/setup-macos-toolchain/action.yml b/.github/actions/setup-macos-toolchain/action.yml index 51da2f8f..738d703c 100644 --- a/.github/actions/setup-macos-toolchain/action.yml +++ b/.github/actions/setup-macos-toolchain/action.yml @@ -11,5 +11,8 @@ runs: shell: bash run: | brew update - brew install ninja mpich llvm libomp openssl + brew install \ + ninja open-mpi llvm libomp openssl \ + autoconf automake libtool libfabric \ + coreutils gnu-sed grep gawk pkgconf brew link libomp --overwrite --force diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6f15a033..bb70f14e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,7 @@ jobs: - name: Setup environment run: | sudo apt-get update - sudo apt-get install -y gcc-15 g++-15 ninja-build mpich libomp-dev valgrind + sudo apt-get install -y gcc-15 g++-15 ninja-build openmpi-bin libopenmpi-dev libomp-dev valgrind python3 -m pip install -r requirements.txt - name: ccache uses: hendrikmuhs/ccache-action@v1.2 diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 8b330f92..ca696553 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -63,8 +63,8 @@ jobs: with: path: install name: macos-clang-install - - name: Run func tests (MPI) - run: scripts/run_tests.py --running-type="processes" --counts 1 2 3 4 + - name: Run func tests (MPI/OSH) + run: scripts/run_tests.py --running-type="processes" --counts 1 2 3 4 --additional-mpi-args="--oversubscribe" env: PPC_NUM_THREADS: 1 PPC_TASK_MAX_TIME: 30 diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 9452b3d8..50ef936d 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -19,6 +19,8 @@ jobs: - uses: actions/checkout@v6 - name: Setup environment run: | + apt-get update + apt-get install -y --no-install-recommends openmpi-bin libopenmpi-dev python3 -m pip install -r requirements.txt --break-system-packages --ignore-installed - name: Download installed package uses: ./.github/actions/download-install diff --git a/.github/workflows/static-analysis-pr.yml b/.github/workflows/static-analysis-pr.yml index fa40d662..ded3a301 100644 --- a/.github/workflows/static-analysis-pr.yml +++ b/.github/workflows/static-analysis-pr.yml @@ -66,6 +66,12 @@ jobs: create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev + - name: CMake configure run: > cmake -S . -B build -G Ninja @@ -117,6 +123,12 @@ jobs: create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev + - name: CMake configure run: > cmake -S . -B build -G Ninja diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 34a25dee..42cc0ceb 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -35,6 +35,11 @@ jobs: key: ${{ runner.os }}-gcc create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev - name: CMake configure run: > cmake -S . -B build -G Ninja @@ -86,7 +91,12 @@ jobs: with: path: install name: ubuntu-gcc-install-${{ matrix.os }} - - name: Run func tests (MPI) + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev + - name: Run func tests (MPI/OSH) run: scripts/run_tests.py --running-type="processes" --counts 1 2 3 4 --additional-mpi-args="--oversubscribe" env: PPC_NUM_THREADS: 1 @@ -139,6 +149,11 @@ jobs: key: ${{ runner.os }}-clang create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev - name: CMake configure run: > cmake -S . -B build -G Ninja @@ -182,7 +197,12 @@ jobs: with: path: install name: ubuntu-clang-install-${{ matrix.os }} - - name: Run func tests (MPI) + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev + - name: Run func tests (MPI/OSH) run: scripts/run_tests.py --running-type="processes" --counts 1 2 3 4 --additional-mpi-args="--oversubscribe" env: PPC_NUM_THREADS: 1 @@ -237,6 +257,11 @@ jobs: key: ${{ runner.os }}-clang create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev - name: CMake configure run: > cmake -S . -B build -G Ninja @@ -282,7 +307,12 @@ jobs: with: path: install name: ubuntu-clang-sanitizer-install-${{ matrix.os }} - - name: Run tests (MPI) + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev + - name: Run tests (MPI/OSH) run: scripts/run_tests.py --running-type="processes" --counts 2 --additional-mpi-args="--oversubscribe" env: PPC_NUM_THREADS: 2 @@ -347,6 +377,11 @@ jobs: key: ${{ runner.os }}-gcc create-symlink: true max-size: 1G + - name: Install Open MPI/OSH dependencies + run: > + apt-get update && + apt-get install -y --no-install-recommends + openmpi-bin libopenmpi-dev - name: CMake configure run: > cmake -S . -B build -G Ninja @@ -360,7 +395,7 @@ jobs: - name: Install project run: | cmake --build build --target install -- --quiet - - name: Run tests (MPI) + - name: Run tests (MPI/OSH) run: scripts/run_tests.py --running-type="processes" --additional-mpi-args="--oversubscribe" env: PPC_NUM_PROC: 2 diff --git a/.gitmodules b/.gitmodules index 601a9e30..67d9ccea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "3rdparty/benchmark"] path = 3rdparty/benchmark url = https://github.com/google/benchmark +[submodule "3rdparty/SOS"] + path = 3rdparty/SOS + url = https://github.com/Sandia-OpenSHMEM/SOS diff --git a/3rdparty/SOS b/3rdparty/SOS new file mode 160000 index 00000000..45e6e7eb --- /dev/null +++ b/3rdparty/SOS @@ -0,0 +1 @@ +Subproject commit 45e6e7eb1a9b099a418237a9e677eb6603222c84 diff --git a/CMakeLists.txt b/CMakeLists.txt index 83e02781..8984d425 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,18 @@ if(PPC_BUILD_COMPONENTS) # ################ Parallel programming technologies ################# message(STATUS "PPC step: Setup parallel programming technologies") - foreach(dep mpi openmp onetbb) + set(PPC_PARALLEL_TECHNOLOGIES mpi openmp onetbb) + set(ppc_osh_supported OFF) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(ppc_osh_supported ON) + set(ppc_osh_backend "openmpi") + list(APPEND PPC_PARALLEL_TECHNOLOGIES osh) + elseif(APPLE) + set(ppc_osh_supported ON) + set(ppc_osh_backend "sos") + list(APPEND PPC_PARALLEL_TECHNOLOGIES osh) + endif() + foreach(dep ${PPC_PARALLEL_TECHNOLOGIES}) include(cmake/${dep}.cmake) endforeach() diff --git a/cmake/functions.cmake b/cmake/functions.cmake index 4a89f367..5e3e9fea 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -16,6 +16,9 @@ function(add_tests test_flag exec_target subdir) # Gather all source files under tests/ file(GLOB_RECURSE src_files "${TEST_DIR}/${subdir}/*.cpp" "${TEST_DIR}/${subdir}/*.cxx" "${TEST_DIR}/${subdir}/*.cc") + if(NOT ppc_osh_supported) + list(FILTER src_files EXCLUDE REGEX "(/|\\\\)osh\\.(cpp|cxx|cc)$") + endif() target_sources(${exec_target} PRIVATE ${src_files}) list(APPEND TEST_EXECUTABLES ${exec_target}) set(TEST_EXECUTABLES @@ -34,6 +37,10 @@ function(setup_implementation) cmake_parse_arguments(SETUP "" # no plain options "NAME;PROJ_NAME;BASE_DIR" "TESTS" ${ARGN}) + if(SETUP_NAME STREQUAL "osh" AND NOT ppc_osh_supported) + return() + endif() + # skip if impl dir doesn't exist set(IMP_DIR "${SETUP_BASE_DIR}/${SETUP_NAME}") if(NOT EXISTS "${IMP_DIR}") diff --git a/cmake/mpi.cmake b/cmake/mpi.cmake index 498b5450..bdf67920 100644 --- a/cmake/mpi.cmake +++ b/cmake/mpi.cmake @@ -1,6 +1,20 @@ include_guard() -find_package(MPI REQUIRED COMPONENTS CXX) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + find_program(ppc_openmpi_c_wrapper NAMES mpicc.openmpi mpicc) + find_program(ppc_openmpi_cxx_wrapper NAMES mpicxx.openmpi mpicxx + mpic++.openmpi mpic++) + if(ppc_openmpi_c_wrapper AND ppc_openmpi_cxx_wrapper) + set(MPI_C_COMPILER + "${ppc_openmpi_c_wrapper}" + CACHE FILEPATH "MPI C compiler wrapper" FORCE) + set(MPI_CXX_COMPILER + "${ppc_openmpi_cxx_wrapper}" + CACHE FILEPATH "MPI CXX compiler wrapper" FORCE) + endif() +endif() + +find_package(MPI REQUIRED COMPONENTS C CXX) if(NOT MPI_FOUND) message(FATAL_ERROR "MPI NOT FOUND") endif() diff --git a/cmake/osh.cmake b/cmake/osh.cmake new file mode 100644 index 00000000..5b6b8e3f --- /dev/null +++ b/cmake/osh.cmake @@ -0,0 +1,196 @@ +include_guard() + +set(osh_target OSH::OSH) + +function(ppc_configure_openmpi_osh) + if(TARGET ${osh_target}) + return() + endif() + + set(osh_include_hints ${MPI_CXX_INCLUDE_DIRS}) + find_path( + OSH_INCLUDE_DIR + NAMES shmem.h + HINTS ${osh_include_hints}) + + set(osh_library_hints) + foreach(mpi_library IN LISTS MPI_CXX_LIBRARIES) + get_filename_component(mpi_library_dir "${mpi_library}" DIRECTORY) + if(mpi_library_dir) + list(APPEND osh_library_hints "${mpi_library_dir}") + endif() + endforeach() + list(REMOVE_DUPLICATES osh_library_hints) + + find_library( + OSH_LIBRARY + NAMES oshmem + HINTS ${osh_library_hints}) + + if(NOT OSH_INCLUDE_DIR OR NOT OSH_LIBRARY) + message( + FATAL_ERROR + "OSH was selected, but Open MPI OSH was not found. Install Open MPI with OSH support, for example: sudo apt install openmpi-bin libopenmpi-dev" + ) + endif() + + add_library(${osh_target} UNKNOWN IMPORTED GLOBAL) + set_target_properties( + ${osh_target} PROPERTIES IMPORTED_LOCATION "${OSH_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${OSH_INCLUDE_DIR}") + target_link_libraries(${osh_target} INTERFACE MPI::MPI_CXX) +endfunction() + +function(ppc_configure_sos_osh) + if(TARGET ${osh_target}) + return() + endif() + + set(sos_source_dir "${CMAKE_SOURCE_DIR}/3rdparty/SOS") + if(NOT EXISTS "${sos_source_dir}/autogen.sh") + message( + FATAL_ERROR + "SOS submodule is missing. Initialize submodules with: git submodule update --init --recursive" + ) + endif() + if(NOT EXISTS "${sos_source_dir}/modules/tests-sos/test/Makefile.am") + message( + FATAL_ERROR + "SOS tests-sos submodule is missing. Initialize submodules with: git submodule update --init --recursive" + ) + endif() + + find_program(sos_bash bash REQUIRED) + find_program(sos_make NAMES gmake make REQUIRED) + find_program(sos_pkg_config pkg-config REQUIRED) + find_program(sos_ccache ccache) + + execute_process( + COMMAND "${sos_pkg_config}" --variable=prefix libfabric + RESULT_VARIABLE sos_pkg_config_result + OUTPUT_VARIABLE sos_ofi_prefix + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT sos_pkg_config_result EQUAL 0 OR sos_ofi_prefix STREQUAL "") + message( + FATAL_ERROR + "SOS OSH backend requires libfabric. Install it, for example: brew install libfabric" + ) + endif() + + include(ProcessorCount) + include(ExternalProject) + ProcessorCount(sos_parallel_jobs) + if(sos_parallel_jobs EQUAL 0) + set(sos_parallel_jobs 2) + endif() + + set(sos_prefix "${CMAKE_BINARY_DIR}/ppc_sos") + set(sos_install_dir "${sos_prefix}/install") + set(sos_build_source_dir "${sos_prefix}/source") + set(sos_build_dir "${sos_prefix}/build") + set(sos_library + "${sos_install_dir}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sma${CMAKE_SHARED_LIBRARY_SUFFIX}" + ) + set(sos_versioned_library + "${sos_install_dir}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sma.0${CMAKE_SHARED_LIBRARY_SUFFIX}" + ) + + file(MAKE_DIRECTORY "${sos_install_dir}/include" "${sos_install_dir}/lib") + + set(sos_c_compiler "${MPI_C_COMPILER}") + set(sos_cxx_compiler "${MPI_CXX_COMPILER}") + set(sos_ccache_env) + if(sos_ccache) + set(sos_c_compiler "${sos_ccache} ${MPI_C_COMPILER}") + set(sos_cxx_compiler "${sos_ccache} ${MPI_CXX_COMPILER}") + set(sos_ccache_env + "CCACHE_BASEDIR='${CMAKE_SOURCE_DIR}' CCACHE_NOHASHDIR=1 ") + endif() + + set(sos_configure_args + "--prefix='${sos_install_dir}'" + "--with-ofi='${sos_ofi_prefix}'" + --enable-pmi-mpi + --disable-fortran + --disable-static + --enable-shared + "CC='${sos_c_compiler}'" + "CXX='${sos_cxx_compiler}'") + list(JOIN sos_configure_args " " sos_configure_args_line) + + string( + CONCAT sos_configure_command + "cd '${sos_build_source_dir}' && ./autogen.sh" + " && mkdir -p '${sos_build_dir}'" + " && cd '${sos_build_dir}'" + " && ${sos_ccache_env}'${sos_build_source_dir}/configure' " + "${sos_configure_args_line}") + string( + CONCAT sos_install_command + "${sos_ccache_env}'${sos_make}' -C '${sos_build_dir}' install" + " && install_name_tool -id '@rpath/libsma.0.dylib' " + "'${sos_versioned_library}'") + + ExternalProject_Add( + ppc_sos + PREFIX "${sos_prefix}" + SOURCE_DIR "${sos_build_source_dir}" + DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E rm -rf "${sos_build_source_dir}" + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${sos_source_dir}" + "${sos_build_source_dir}" + CONFIGURE_COMMAND "${sos_bash}" -lc "${sos_configure_command}" + BUILD_COMMAND + "${sos_bash}" -lc + "${sos_ccache_env}'${sos_make}' -C '${sos_build_dir}' -j${sos_parallel_jobs}" + INSTALL_COMMAND "${sos_bash}" -lc "${sos_install_command}" + BUILD_BYPRODUCTS "${sos_library}" "${sos_versioned_library}" + ${PPC_EXTERNAL_PROJECT_LOG_ARGS}) + + add_library(${osh_target} UNKNOWN IMPORTED GLOBAL) + add_dependencies(${osh_target} ppc_sos) + set_target_properties( + ${osh_target} + PROPERTIES IMPORTED_LOCATION "${sos_library}" + INTERFACE_INCLUDE_DIRECTORIES "${sos_install_dir}/include" + INTERFACE_LINK_OPTIONS "-Wl,-rpath,@loader_path/../lib") + target_link_libraries(${osh_target} INTERFACE MPI::MPI_CXX) + + install( + DIRECTORY "${sos_install_dir}/" + DESTINATION "." + USE_SOURCE_PERMISSIONS) +endfunction() + +function(ppc_configure_osh) + if(TARGET ${osh_target}) + return() + endif() + + if(NOT ppc_osh_supported) + return() + endif() + + if(ppc_osh_backend STREQUAL "openmpi") + ppc_configure_openmpi_osh() + elseif(ppc_osh_backend STREQUAL "sos") + ppc_configure_sos_osh() + else() + message(FATAL_ERROR "Unknown OSH backend: ${ppc_osh_backend}") + endif() +endfunction() + +function(ppc_link_osh target) + if(NOT ppc_osh_supported) + return() + endif() + + ppc_configure_osh() + if(NOT TARGET ${osh_target}) + message(FATAL_ERROR "OSH backend target was not configured.") + endif() + + target_link_libraries(${target} PUBLIC ${osh_target}) + if(TARGET ppc_sos) + add_dependencies(${target} ppc_sos) + endif() +endfunction() diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index d7e6fd76..f2793db1 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -18,6 +18,14 @@ foreach(subd ${subdirs}) list(APPEND FUNC_TESTS_SOURCE_FILES ${TMP_FUNC_TESTS_SOURCE_FILES}) endforeach() +if(ppc_osh_supported) + list(REMOVE_ITEM LIB_SOURCE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/util/src/osh_runtime_disabled.cpp") +else() + list(REMOVE_ITEM LIB_SOURCE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/util/src/osh_runtime.cpp") +endif() + project(${exec_func_lib}) add_library(${exec_func_lib} STATIC ${LIB_SOURCE_FILES}) set_target_properties(${exec_func_lib} PROPERTIES LINKER_LANGUAGE CXX) @@ -41,6 +49,10 @@ foreach( cmake_language(CALL "ppc_link_${link}" ${exec_func_lib}) endforeach() +if(ppc_osh_supported) + ppc_link_osh(${exec_func_lib}) +endif() + add_executable(${exec_func_tests} ${FUNC_TESTS_SOURCE_FILES}) target_link_libraries(${exec_func_tests} PUBLIC ${exec_func_lib}) diff --git a/modules/runners/src/runners.cpp b/modules/runners/src/runners.cpp index 598fef0f..b5b74105 100644 --- a/modules/runners/src/runners.cpp +++ b/modules/runners/src/runners.cpp @@ -17,6 +17,7 @@ #include #include "oneapi/tbb/global_control.h" +#include "util/include/osh_runtime.hpp" #include "util/include/util.hpp" namespace ppc::runners { @@ -170,6 +171,15 @@ int Init(int argc, char **argv) { const int status = RunAllTestsSafely(); + if (ppc::util::OshRuntime::IsInitialized()) { + ppc::util::OshRuntime::Finalize(); + } + + int mpi_finalized = 0; + if (MPI_Finalized(&mpi_finalized) == MPI_SUCCESS && mpi_finalized != 0) { + return status; + } + const int finalize_res = MPI_Finalize(); if (finalize_res != MPI_SUCCESS) { std::cerr << std::format("[ ERROR ] MPI_Finalize failed with code {}", finalize_res) << '\n'; diff --git a/modules/task/include/task.hpp b/modules/task/include/task.hpp index b6eaf59f..98a53518 100644 --- a/modules/task/include/task.hpp +++ b/modules/task/include/task.hpp @@ -25,6 +25,8 @@ enum class TypeOfTask : uint8_t { kALL, /// MPI (Message Passing Interface) kMPI, + /// OSH (PGAS / one-sided distributed-memory communication) + kOSH, /// OpenMP (Open Multi-Processing) kOMP, /// Sequential implementation @@ -38,10 +40,11 @@ enum class TypeOfTask : uint8_t { }; using TaskMapping = std::pair; -using TaskMappingArray = std::array; +using TaskMappingArray = std::array; inline constexpr TaskMappingArray kTaskTypeMappings = {{{TypeOfTask::kALL, "all"}, {TypeOfTask::kMPI, "mpi"}, + {TypeOfTask::kOSH, "osh"}, {TypeOfTask::kOMP, "omp"}, {TypeOfTask::kSEQ, "seq"}, {TypeOfTask::kSTL, "stl"}, diff --git a/modules/task/tests/task_tests.cpp b/modules/task/tests/task_tests.cpp index a2755468..75d11c38 100644 --- a/modules/task/tests/task_tests.cpp +++ b/modules/task/tests/task_tests.cpp @@ -185,7 +185,7 @@ TEST(TaskTest, GetStringTaskTypeUnknownTypeWithValidFile) { ScopedFile cleaner(path); std::ofstream file(path); file - << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "tbb": "enabled", "seq": "enabled"}})"; + << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "osh": "enabled", "tbb": "enabled", "seq": "enabled"}})"; file.close(); EXPECT_NO_THROW({ GetStringTaskType(TypeOfTask::kUnknown, path); }); } @@ -204,13 +204,14 @@ TEST(TaskTest, GetStringTaskTypeEachTypeWithValidFile) { ScopedFile cleaner(path); std::ofstream file(path); file - << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "tbb": "enabled", "seq": "enabled"}})"; + << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "osh": "enabled", "tbb": "enabled", "seq": "enabled"}})"; file.close(); EXPECT_EQ(GetStringTaskType(TypeOfTask::kALL, path), "all_enabled"); EXPECT_EQ(GetStringTaskType(TypeOfTask::kSTL, path), "stl_enabled"); EXPECT_EQ(GetStringTaskType(TypeOfTask::kOMP, path), "omp_enabled"); EXPECT_EQ(GetStringTaskType(TypeOfTask::kMPI, path), "mpi_enabled"); + EXPECT_EQ(GetStringTaskType(TypeOfTask::kOSH, path), "osh_enabled"); EXPECT_EQ(GetStringTaskType(TypeOfTask::kTBB, path), "tbb_enabled"); EXPECT_EQ(GetStringTaskType(TypeOfTask::kSEQ, path), "seq_enabled"); } diff --git a/modules/util/include/func_test_util.hpp b/modules/util/include/func_test_util.hpp index b1e4d44b..0d8fe20a 100644 --- a/modules/util/include/func_test_util.hpp +++ b/modules/util/include/func_test_util.hpp @@ -13,6 +13,7 @@ #include #include "task/include/task.hpp" +#include "util/include/osh_runtime.hpp" #include "util/include/task_descriptor_util.hpp" #include "util/include/util.hpp" @@ -80,8 +81,8 @@ class BaseRunFuncTests : public ::testing::TestWithParam &test_param) { const auto &descriptor = GetTaskDescriptor(test_param); - return IsTestDisabled(descriptor) || ShouldSkipNonMpiTask(descriptor); + return IsTestDisabled(descriptor) || ShouldSkipProcessTask(descriptor); } /// @brief Initializes task instance and runs it through the full pipeline. @@ -119,7 +120,7 @@ class BaseRunFuncTests : public ::testing::TestWithParamValidation()); - SynchronizeMpiRanks(); + SynchronizeTaskRanks(task_->GetDynamicTypeOfTask()); EXPECT_TRUE(task_->PreProcessing()); } @@ -134,6 +135,14 @@ class BaseRunFuncTests : public ::testing::TestWithParam task_; + + static void SynchronizeTaskRanks(ppc::task::TypeOfTask type) { + if (IsOshTaskType(type)) { + ppc::util::OshRuntime::BarrierAll(); + return; + } + SynchronizeMpiRanks(); + } }; namespace detail { diff --git a/modules/util/include/osh_runtime.hpp b/modules/util/include/osh_runtime.hpp new file mode 100644 index 00000000..5cec8d90 --- /dev/null +++ b/modules/util/include/osh_runtime.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace ppc::util { + +class OshRuntime { + public: + OshRuntime() = delete; + + static void EnsureInitialized(); + static void Finalize(); + static bool IsInitialized(); + static int MyPe(); + static int NumPes(); + static void BarrierAll(); + static void GlobalExit(int status); +}; + +} // namespace ppc::util diff --git a/modules/util/include/perf_test_util.hpp b/modules/util/include/perf_test_util.hpp index e4c4b29d..ea4022f3 100644 --- a/modules/util/include/perf_test_util.hpp +++ b/modules/util/include/perf_test_util.hpp @@ -21,6 +21,7 @@ #include #include "task/include/task.hpp" +#include "util/include/osh_runtime.hpp" #include "util/include/task_descriptor_util.hpp" #include "util/include/util.hpp" @@ -67,6 +68,14 @@ inline std::function MakeTechnologyTimer(ppc::task::TypeOfTask task_ty if (task_type == ppc::task::TypeOfTask::kMPI || task_type == ppc::task::TypeOfTask::kALL) { return [] -> double { return GetTimeMPI(); }; } + if (task_type == ppc::task::TypeOfTask::kOSH) { + const auto t0 = std::chrono::high_resolution_clock::now(); + return [t0] -> double { + const auto now = std::chrono::high_resolution_clock::now(); + const auto ns = std::chrono::duration_cast(now - t0).count(); + return static_cast(ns) * 1e-9; + }; + } if (task_type == ppc::task::TypeOfTask::kOMP) { return [] -> double { return omp_get_wtime(); }; } @@ -94,6 +103,14 @@ inline double MaxElapsedTimeAcrossMpiRanks(double elapsed, ppc::task::TypeOfTask return max_elapsed; } +inline void SynchronizeTaskRanks(ppc::task::TypeOfTask task_type) { + if (task_type == ppc::task::TypeOfTask::kOSH) { + ppc::util::OshRuntime::BarrierAll(); + return; + } + SynchronizeMpiRanks(); +} + inline void SkipBenchmarkWithError(benchmark::State &state, const char *message) noexcept { try { state.SkipWithError(message); @@ -118,7 +135,7 @@ double RunTaskForBenchmark(const ppc::task::TaskPtr &task) { task->Validation(); task->PreProcessing(); - SynchronizeMpiRanks(); + SynchronizeTaskRanks(task_type); const double begin = timer(); task->Run(); const double elapsed = timer() - begin; @@ -210,7 +227,7 @@ class BaseRunPerfTests : public ::testing::TestWithParamGetStateOfTesting() = ppc::task::StateOfTesting::kPerf; - SynchronizeMpiRanks(); + detail::SynchronizeTaskRanks(descriptor.type); detail::RunTaskForValidation(task_); OutType output_data = task_->GetOutput(); diff --git a/modules/util/include/task_descriptor_util.hpp b/modules/util/include/task_descriptor_util.hpp index 63131c69..8dd3959f 100644 --- a/modules/util/include/task_descriptor_util.hpp +++ b/modules/util/include/task_descriptor_util.hpp @@ -32,4 +32,8 @@ inline bool IsMpiTaskType(ppc::task::TypeOfTask type) { return type == ppc::task::TypeOfTask::kMPI || type == ppc::task::TypeOfTask::kALL; } +inline bool IsOshTaskType(ppc::task::TypeOfTask type) { + return type == ppc::task::TypeOfTask::kOSH; +} + } // namespace ppc::util diff --git a/modules/util/src/osh_runtime.cpp b/modules/util/src/osh_runtime.cpp new file mode 100644 index 00000000..185a2715 --- /dev/null +++ b/modules/util/src/osh_runtime.cpp @@ -0,0 +1,80 @@ +#include "util/include/osh_runtime.hpp" + +#include + +#include +#include +#include + +namespace { + +std::mutex g_runtime_mutex; +bool g_initialized = false; +bool g_finalized = false; +bool g_atexit_registered = false; + +void FinalizeAtExit() noexcept { + try { + ppc::util::OshRuntime::Finalize(); + } catch (...) { + } +} + +void RegisterFinalizeAtExit() { + if (!g_atexit_registered) { + std::atexit(FinalizeAtExit); + g_atexit_registered = true; + } +} + +} // namespace + +void ppc::util::OshRuntime::EnsureInitialized() { + std::lock_guard lock(g_runtime_mutex); + if (g_initialized) { + return; + } + if (g_finalized) { + throw std::runtime_error("OSH runtime was already finalized in this process"); + } + + shmem_init(); + g_initialized = true; + RegisterFinalizeAtExit(); +} + +void ppc::util::OshRuntime::Finalize() { + std::lock_guard lock(g_runtime_mutex); + if (!g_initialized || g_finalized) { + return; + } + + shmem_finalize(); + g_initialized = false; + g_finalized = true; +} + +bool ppc::util::OshRuntime::IsInitialized() { + std::lock_guard lock(g_runtime_mutex); + return g_initialized; +} + +int ppc::util::OshRuntime::MyPe() { + EnsureInitialized(); + return shmem_my_pe(); +} + +int ppc::util::OshRuntime::NumPes() { + EnsureInitialized(); + return shmem_n_pes(); +} + +void ppc::util::OshRuntime::BarrierAll() { + EnsureInitialized(); + shmem_barrier_all(); +} + +void ppc::util::OshRuntime::GlobalExit(int status) { + EnsureInitialized(); + shmem_global_exit(status); +} diff --git a/modules/util/src/osh_runtime_disabled.cpp b/modules/util/src/osh_runtime_disabled.cpp new file mode 100644 index 00000000..9e0753bd --- /dev/null +++ b/modules/util/src/osh_runtime_disabled.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "util/include/osh_runtime.hpp" + +void ppc::util::OshRuntime::EnsureInitialized() { + throw std::runtime_error("OSH runtime is not available on this platform"); +} + +void ppc::util::OshRuntime::Finalize() {} + +bool ppc::util::OshRuntime::IsInitialized() { + return false; +} + +int ppc::util::OshRuntime::MyPe() { + throw std::runtime_error("OSH runtime is not available on this platform"); +} + +int ppc::util::OshRuntime::NumPes() { + throw std::runtime_error("OSH runtime is not available on this platform"); +} + +void ppc::util::OshRuntime::BarrierAll() { + throw std::runtime_error("OSH runtime is not available on this platform"); +} + +void ppc::util::OshRuntime::GlobalExit(int status) { + std::exit(status); +} diff --git a/modules/util/src/util.cpp b/modules/util/src/util.cpp index d29c9277..dbe186a1 100644 --- a/modules/util/src/util.cpp +++ b/modules/util/src/util.cpp @@ -70,9 +70,9 @@ bool ppc::util::IsUnderMpirun() { void ppc::util::ConfigureMpiEnvironment() { #ifdef __APPLE__ - // Open MPI 5 can emit mmap backing-file probe warnings for macOS TMPDIR paths. + // Open MPI 5's POSIX shmem component is fragile on some macOS TMPDIR paths. if (!env::get("OMPI_MCA_shmem").has_value()) { - env::detail::set_environment_variable("OMPI_MCA_shmem", "posix"); + env::detail::set_environment_variable("OMPI_MCA_shmem", "^posix"); } #endif } diff --git a/scripts/check_task_backend_apis.py b/scripts/check_task_backend_apis.py index 79add074..b20d70e6 100644 --- a/scripts/check_task_backend_apis.py +++ b/scripts/check_task_backend_apis.py @@ -107,6 +107,15 @@ def compile_include_regex(headers: tuple[str, ...]) -> re.Pattern: ApiPattern("MPI", re.compile(r"\bMPI_[A-Za-z0-9_]*\b"), True), ApiPattern("MPI", re.compile(r"\bMPI::"), True), ], + "osh": [ + ApiPattern( + "OSH", + re.compile(r"^\s*#\s*include\s*[<\"]shmem\.h[>\"]"), + False, + ), + ApiPattern("OSH", re.compile(r"\bshmem_[A-Za-z0-9_]*\b"), True), + ApiPattern("OSH", re.compile(r"\bSHMEM_[A-Za-z0-9_]*\b"), True), + ], "cpp_thread": [ ApiPattern( "C++ thread API", @@ -132,15 +141,16 @@ def compile_include_regex(headers: tuple[str, ...]) -> re.Pattern: } -API_ORDER = ("openmp", "tbb", "mpi", "cpp_thread") +API_ORDER = ("openmp", "tbb", "mpi", "osh", "cpp_thread") BACKEND_RULES = { - "seq": {"openmp", "tbb", "mpi", "cpp_thread"}, - "omp": {"tbb", "mpi", "cpp_thread"}, - "tbb": {"openmp", "mpi", "cpp_thread"}, - "mpi": {"openmp", "tbb", "cpp_thread"}, - "stl": {"openmp", "tbb", "mpi"}, + "seq": {"openmp", "tbb", "mpi", "osh", "cpp_thread"}, + "omp": {"tbb", "mpi", "osh", "cpp_thread"}, + "tbb": {"openmp", "mpi", "osh", "cpp_thread"}, + "mpi": {"openmp", "tbb", "osh", "cpp_thread"}, + "osh": {"openmp", "tbb", "mpi", "cpp_thread"}, + "stl": {"openmp", "tbb", "mpi", "osh"}, "common": set(API_ORDER), } @@ -292,12 +302,15 @@ def get_backend(path: Path) -> str | None: except ValueError: return None - backend_index = tasks_index + 1 - if backend_index >= len(parts) or parts[tasks_index] == "common": + if tasks_index >= len(parts) or parts[tasks_index] == "common": return None - backend = parts[backend_index] - return backend if backend in BACKEND_RULES else None + for backend_index, backend in enumerate(parts): + if backend_index <= tasks_index: + continue + if backend in BACKEND_RULES: + return backend + return None def is_source_file(path: Path) -> bool: diff --git a/scripts/run_tests.py b/scripts/run_tests.py index bdbfc228..733fd982 100755 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -63,8 +63,10 @@ def __init__(self, build_dir="build", verbose=False): if platform.system() == "Windows": self.mpi_exec = "mpiexec" + self.osh_exec = "mpiexec" else: - self.mpi_exec = "mpirun" + self.mpi_exec = shutil.which("mpirun.openmpi") or "mpirun" + self.osh_exec = shutil.which("oshrun") or "oshrun" self.platform = platform.system() # Detect MPI implementation to choose compatible flags @@ -108,6 +110,7 @@ def setup_env(self, ppc_env): install_bin_dir = project_path / "install" / "bin" if install_bin_dir.exists(): self.work_dir = install_bin_dir + self.__select_mpi_launcher() return bin_dir = build_dir if build_dir.name == "bin" else build_dir / "bin" @@ -117,6 +120,7 @@ def setup_env(self, ppc_env): "Build the project or pass a correct '--build-dir' (e.g. 'build', 'build_seq', or 'build/bin')." ) self.work_dir = bin_dir + self.__select_mpi_launcher() def __run_exec(self, command, extra_env=None): if self.verbose: @@ -160,11 +164,50 @@ def __detect_mpi_impl(self): return "mpich", "-n" return "unknown", "-np" - def __build_mpi_cmd(self, ppc_num_proc, additional_mpi_args, extra_env=None): + def __select_mpi_launcher(self): + if self.platform == "Windows" or self.__build_dir_path is None: + return + + project_path = Path(self.__get_project_path()) + mpi_candidates = [] + if self.work_dir is not None: + mpi_candidates.append(self.work_dir / "mpirun") + mpi_candidates.append(project_path / "install" / "bin" / "mpirun") + for launcher in mpi_candidates: + if launcher.is_file() and os.access(launcher, os.X_OK): + self.mpi_exec = str(launcher) + self.mpi_env_mode, self.mpi_np_flag = self.__detect_mpi_impl() + break + + osh_candidates = [] + if self.work_dir is not None: + osh_candidates.append(self.work_dir / "oshrun") + osh_candidates.append( + self.__build_dir_path / "ppc_sos" / "install" / "bin" / "oshrun" + ) + osh_candidates.append(project_path / "install" / "bin" / "oshrun") + for launcher in osh_candidates: + if launcher.is_file() and os.access(launcher, os.X_OK): + self.osh_exec = str(launcher) + return + + def __supports_osh(self): + return self.platform != "Windows" + + def __build_mpi_cmd( + self, ppc_num_proc, additional_mpi_args, extra_env=None, launcher=None + ): mpi_env = self.__ppc_env.copy() if extra_env: mpi_env.update(extra_env) - base = [self.mpi_exec] + shlex.split(additional_mpi_args) + additional_args = shlex.split(additional_mpi_args) + if self.mpi_env_mode == "mpich": + additional_args = [ + arg + for arg in additional_args + if arg not in ("--oversubscribe", "-oversubscribe") + ] + base = [launcher or self.mpi_exec] + additional_args forwarded_env = [ "PPC_NUM_THREADS", "OMP_NUM_THREADS", @@ -233,6 +276,7 @@ def __get_gtest_settings(repeats_count, type_task): type_task_patterns = { "_all_": ["_all_", "AllEnabled"], "_mpi_": ["_mpi_", "MpiEnabled"], + "_osh_": ["_osh_", "OSHEnabled"], "_omp_": ["_omp_", "OmpEnabled"], "_seq_": ["_seq_", "SeqEnabled"], "_stl_": ["_stl_", "StlEnabled"], @@ -290,6 +334,15 @@ def run_processes(self, additional_mpi_args): + [str(self.work_dir / "ppc_func_tests")] + self.__get_gtest_settings(1, "_" + task_type + "_") ) + if self.__supports_osh(): + osh_running = self.__build_mpi_cmd( + ppc_num_proc, additional_mpi_args, launcher=self.osh_exec + ) + self.__run_exec( + osh_running + + [str(self.work_dir / "ppc_func_tests")] + + self.__get_gtest_settings(1, "_osh_") + ) def run_performance(self): output_dir = self.__benchmark_output_dir() @@ -297,15 +350,25 @@ def run_performance(self): shutil.rmtree(output_dir) if not self.__ppc_env.get("PPC_ASAN_RUN"): - for category, task_type in [ + perf_jobs = [ ("threads", "all"), ("processes", "mpi"), ("processes", "seq"), - ]: + ] + if self.__supports_osh(): + perf_jobs.insert(2, ("processes", "osh")) + for category, task_type in perf_jobs: extra_env = self.__get_benchmark_env(category, task_type) - mpi_running = self.__build_mpi_cmd(self.__ppc_num_proc, "", extra_env) + launcher = ( + self.osh_exec + if category == "processes" and task_type == "osh" + else None + ) + runner = self.__build_mpi_cmd( + self.__ppc_num_proc, "", extra_env, launcher + ) self.__run_exec( - mpi_running + runner + [str(self.work_dir / "ppc_perf_tests")] + self.__get_performance_gtest_settings(), extra_env, diff --git a/tasks/CMakeLists.txt b/tasks/CMakeLists.txt index a20f74ab..5e8573fc 100644 --- a/tasks/CMakeLists.txt +++ b/tasks/CMakeLists.txt @@ -17,7 +17,14 @@ if(USE_PERF_TESTS) endif() # ——— List of implementations ———————————————————————————————————————— -set(PPC_IMPLEMENTATIONS "all;mpi;omp;seq;stl;tbb" CACHE STRING "Implementations to build (semicolon-separated)") +if(ppc_osh_supported) + set(PPC_DEFAULT_IMPLEMENTATIONS "all;mpi;osh;omp;seq;stl;tbb") +else() + set(PPC_DEFAULT_IMPLEMENTATIONS "all;mpi;omp;seq;stl;tbb") +endif() +set(PPC_IMPLEMENTATIONS + "${PPC_DEFAULT_IMPLEMENTATIONS}" + CACHE STRING "Implementations to build (semicolon-separated)") # ——— List of tasks ——————————————————————————————————————————————————— set(PPC_TASKS "all" CACHE STRING "Tasks to build (semicolon-separated, or 'all')") diff --git a/tasks/common/runners/performance.cpp b/tasks/common/runners/performance.cpp index 20da6520..6d33ed7b 100644 --- a/tasks/common/runners/performance.cpp +++ b/tasks/common/runners/performance.cpp @@ -22,6 +22,7 @@ #include "oneapi/tbb/global_control.h" #include "runners/include/runners.hpp" +#include "util/include/osh_runtime.hpp" #include "util/include/util.hpp" namespace { @@ -211,6 +212,15 @@ int RunPerformanceMain(int argc, char **argv) { status = SynchronizeStatus(RunRegisteredBenchmarks(rank), "Google Benchmark"); } + if (ppc::util::OshRuntime::IsInitialized()) { + ppc::util::OshRuntime::Finalize(); + } + + int mpi_finalized = 0; + if (MPI_Finalized(&mpi_finalized) == MPI_SUCCESS && mpi_finalized != 0) { + return status; + } + const int finalize_res = MPI_Finalize(); if (finalize_res != MPI_SUCCESS) { std::cerr << "[ ERROR ] MPI_Finalize failed with code " << finalize_res << '\n'; diff --git a/tasks/example/processes/t1/osh/include/ops_osh.hpp b/tasks/example/processes/t1/osh/include/ops_osh.hpp new file mode 100644 index 00000000..05ff6cdc --- /dev/null +++ b/tasks/example/processes/t1/osh/include/ops_osh.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "example/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace example_processes_t1 { + +class NesterovATestTaskOSH : public BaseTask { + public: + explicit NesterovATestTaskOSH(const InType &in); + + protected: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace example_processes_t1 diff --git a/tasks/example/processes/t1/osh/src/ops_osh.cpp b/tasks/example/processes/t1/osh/src/ops_osh.cpp new file mode 100644 index 00000000..b14f9e75 --- /dev/null +++ b/tasks/example/processes/t1/osh/src/ops_osh.cpp @@ -0,0 +1,69 @@ +#include "example/processes/t1/osh/include/ops_osh.hpp" + +#include + +#include +#include + +#include "example/common/include/common.hpp" +#include "util/include/osh_runtime.hpp" +#include "util/include/util.hpp" + +namespace example_processes_t1 { + +NesterovATestTaskOSH::NesterovATestTaskOSH(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool NesterovATestTaskOSH::ValidationImpl() { + return (GetInput() > 0) && (GetOutput() == 0); +} + +bool NesterovATestTaskOSH::PreProcessingImpl() { + GetOutput() = 2 * GetInput(); + return GetOutput() > 0; +} + +bool NesterovATestTaskOSH::RunImpl() { + ppc::util::OshRuntime::EnsureInitialized(); + + const int my_pe = shmem_my_pe(); + const int num_pes = shmem_n_pes(); + if (GetInput() == 0 || my_pe < 0 || num_pes <= 0) { + return false; + } + + for (InType i = my_pe; i < GetInput(); i += num_pes) { + for (InType j = 0; j < GetInput(); j++) { + for (InType k = 0; k < GetInput(); k++) { + std::vector tmp(i + j + k, 1); + GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0); + GetOutput() -= i + j + k; + } + } + } + + const int num_threads = ppc::util::GetNumThreads(); + GetOutput() *= num_threads; + + int counter = 0; + for (int i = 0; i < num_threads; i++) { + counter++; + } + + if (counter != 0) { + GetOutput() /= counter; + } + + shmem_barrier_all(); + return GetOutput() > 0; +} + +bool NesterovATestTaskOSH::PostProcessingImpl() { + GetOutput() -= GetInput(); + return GetOutput() > 0; +} + +} // namespace example_processes_t1 diff --git a/tasks/example/processes/t1/tests/functional/osh.cpp b/tasks/example/processes/t1/tests/functional/osh.cpp new file mode 100644 index 00000000..7a912897 --- /dev/null +++ b/tasks/example/processes/t1/tests/functional/osh.cpp @@ -0,0 +1,86 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "example/common/include/common.hpp" +#include "example/processes/t1/osh/include/ops_osh.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace example_processes_t1 { + +class NesterovARunFuncTestsProcessesOSH : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void RunTestCase(const ppc::util::FuncTestParam &test_param) override { + if (ShouldSkipTestCase(test_param)) { + return; + } + + SetInputData(); + ExecuteTest(test_param); + } + + void SetInputData() { + int width = -1; + int height = -1; + int channels = -1; + std::vector img; + { + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example, "pic.ppm"); + auto *data = stbi_load(abs_path.c_str(), &width, &height, &channels, STBI_rgb); + if (data == nullptr) { + throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason())); + } + channels = STBI_rgb; + img = std::vector(data, data + (static_cast(width * height * channels))); + stbi_image_free(data); + if (std::cmp_not_equal(width, height)) { + throw std::runtime_error("width != height: "); + } + } + + input_data_ = width - height + std::min(std::accumulate(img.begin(), img.end(), 0), channels); + } + + bool CheckTestOutputData(OutType &output_data) final { + return (input_data_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_ = 0; +}; + +namespace { + +const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; + +const auto kTestTasksList = + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example, "processes.t1"); + +} // namespace + +TEST_F(NesterovARunFuncTestsProcessesOSH, MatmulFromPicOSHEnabled) { + RunTestCasesWithTag(kTestTasksList, "osh"); +} + +} // namespace example_processes_t1 diff --git a/tasks/example/processes/t1/tests/performance/osh.cpp b/tasks/example/processes/t1/tests/performance/osh.cpp new file mode 100644 index 00000000..c29bc02f --- /dev/null +++ b/tasks/example/processes/t1/tests/performance/osh.cpp @@ -0,0 +1,41 @@ +#include + +#include + +#include "example/common/include/common.hpp" +#include "example/processes/t1/osh/include/ops_osh.hpp" +#include "util/include/perf_test_util.hpp" + +namespace example_processes_t1 { + +class ExampleRunPerfTestProcessesOSH : public ppc::util::BaseRunPerfTests { + protected: + void SetUp() override { + input_data_ = kCount_; + } + + bool CheckTestOutputData(OutType &output_data) final { + return input_data_ == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + const int kCount_ = 100; + InType input_data_{}; +}; + +namespace { + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example, "processes.t1"); + +} // namespace + +TEST_F(ExampleRunPerfTestProcessesOSH, RunPerf) { + std::apply([this](const auto &...test_params) { (ExecuteTest(test_params), ...); }, kAllPerfTasks); +} + +} // namespace example_processes_t1 diff --git a/tasks/example/settings.json b/tasks/example/settings.json index c89c5817..cae62724 100644 --- a/tasks/example/settings.json +++ b/tasks/example/settings.json @@ -3,6 +3,7 @@ "processes": { "t1": { "mpi": "enabled", + "osh": "enabled", "seq": "enabled" }, "t2": {