diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e17446854e..1eb8c8e3228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,9 @@ include(cmake/build_type_helpers.cmake) include(cmake/build_helpers.cmake) include(cmake/install_helpers.cmake) +# Store the used CMAKE_CXX_STANDARD or fallback to the highest supported standard of (c++14, c++17) +ginkgo_set_cxx_standard() + if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") endif() diff --git a/cmake/GinkgoConfig.cmake.in b/cmake/GinkgoConfig.cmake.in index 23b1d25adc1..f1cb7656ffd 100644 --- a/cmake/GinkgoConfig.cmake.in +++ b/cmake/GinkgoConfig.cmake.in @@ -22,6 +22,7 @@ set(GINKGO_DEBUG_POSTFIX "@CMAKE_DEBUG_POSTFIX@") # Compiler configuration set(GINKGO_CXX_COMPILER "@CMAKE_CXX_COMPILER@") set(GINKGO_CXX_COMPILER_VERSION @CMAKE_CXX_COMPILER_VERSION@) +set(GINKGO_CXX_STANDARD "@GINKGO_CXX_STANDARD@") set(GINKGO_CXX_FLAGS "@CMAKE_CXX_FLAGS@") set(GINKGO_CXX_FLAGS_DEBUG "@CMAKE_CXX_FLAGS_DEBUG@") diff --git a/cmake/build_helpers.cmake b/cmake/build_helpers.cmake index a1a1735f84e..945d5e61412 100644 --- a/cmake/build_helpers.cmake +++ b/cmake/build_helpers.cmake @@ -1,5 +1,53 @@ set(GINKGO_LIBRARY_PATH "${PROJECT_BINARY_DIR}/lib") +function(ginkgo_check_std_available std) + if (std EQUAL 23) + set(__std_macro 202302L) + elseif (std EQUAL 20) + set(__std_macro 202002L) + elseif (std EQUAL 17) + set(__std_macro 201703L) + elseif (std EQUAL 14) + set(__std_macro 201402L) + else () + message(FATAL_ERROR "Unsupported CXX standard: ${std}.") + endif () + try_compile(GINKGO_HAVE_CXX${std} + ${Ginkgo_BINARY_DIR} + ${Ginkgo_SOURCE_DIR}/cmake/cxx_std_check.cpp + CMAKE_FLAGS -DCMAKE_CXX_STANDARD=${std} + COMPILE_DEFINITIONS -DCXX_STD=${__std_macro} + ) + set(GINKGO_HAVE_CXX${std} PARENT_SCOPE) +endfunction() + +macro(ginkgo_set_cxx_standard) + set(GINKGO_CXX_STANDARD ${CMAKE_CXX_STANDARD}) + if(NOT GINKGO_CXX_STANDARD) + ginkgo_check_std_available(14) + if (GINKGO_HAVE_CXX14) + set(GINKGO_CXX_STANDARD 14) + endif () + ginkgo_check_std_available(17) + if (GINKGO_HAVE_CXX17) + set(GINKGO_CXX_STANDARD 17) + endif () + endif () + set(__supported_cxx_std 14 17 20 23) + if(NOT GINKGO_CXX_STANDARD IN_LIST __supported_cxx_std) + message(FATAL_ERROR "Unsupported CXX standard: ${GINKGO_CXX_STANDARD}.") + endif () + foreach (__std IN LISTS __supported_cxx_std) + if (GINKGO_CXX_STANDARD GREATER_EQUAL __std) + set(GINKGO_HAVE_CXX${__std} ON) + set(GINKGO_CXX_STANDARD_FEATURE cxx_std_${__std}) + else () + set(GINKGO_HAVE_CXX${__std} OFF) + endif () + endforeach () + unset(__supported_cxx_std) +endmacro() + function(ginkgo_default_includes name) # set include path depending on used interface target_include_directories("${name}" @@ -18,7 +66,7 @@ function(ginkgo_default_includes name) endfunction() function(ginkgo_compile_features name) - target_compile_features("${name}" PUBLIC cxx_std_14) + target_compile_features("${name}" PUBLIC ${GINKGO_CXX_STANDARD_FEATURE}) # we set these properties regardless of the enabled backends, # because unknown properties are ignored set_target_properties("${name}" PROPERTIES HIP_STANDARD 14) diff --git a/cmake/create_test.cmake b/cmake/create_test.cmake index ecb75d5da39..b9c5f599d06 100644 --- a/cmake/create_test.cmake +++ b/cmake/create_test.cmake @@ -32,7 +32,6 @@ function(ginkgo_set_test_target_properties test_target_name test_library_suffix) else() target_link_libraries(${test_target_name} PRIVATE ginkgo_gtest_main${test_library_suffix}) endif() - target_compile_features(${test_target_name} PUBLIC cxx_std_14) # we set these properties regardless of the enabled backends, # because unknown properties are ignored set_target_properties(${test_target_name} PROPERTIES HIP_STANDARD 14) diff --git a/cmake/cxx_std_check.cpp b/cmake/cxx_std_check.cpp new file mode 100644 index 00000000000..8b73a66cea2 --- /dev/null +++ b/cmake/cxx_std_check.cpp @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// +// SPDX-License-Identifier: BSD-3-Clause + +int main() { static_assert(__cplusplus >= CXX_STD); } diff --git a/cmake/get_info.cmake b/cmake/get_info.cmake index 6b904189151..ee24c47c4de 100644 --- a/cmake/get_info.cmake +++ b/cmake/get_info.cmake @@ -110,7 +110,7 @@ set(log_types "detailed_log;minimal_log") foreach(log_type ${log_types}) ginkgo_print_module_footer(${${log_type}} "Ginkgo configuration:") ginkgo_print_foreach_variable(${${log_type}} - "CMAKE_BUILD_TYPE;BUILD_SHARED_LIBS;CMAKE_INSTALL_PREFIX" + "GINKGO_CXX_STANDARD;CMAKE_BUILD_TYPE;BUILD_SHARED_LIBS;CMAKE_INSTALL_PREFIX" "PROJECT_SOURCE_DIR;PROJECT_BINARY_DIR") string(SUBSTRING " diff --git a/cmake/information_helpers.cmake b/cmake/information_helpers.cmake index 0e667a90f02..7c6e8b845a8 100644 --- a/cmake/information_helpers.cmake +++ b/cmake/information_helpers.cmake @@ -112,7 +112,7 @@ macro(ginkgo_pkg_information) # Prepare recursively populated library list list(APPEND GINKGO_INTERFACE_LIBS_FOUND "-lginkgo$<$:${CMAKE_DEBUG_POSTFIX}>") # Prepare recursively populated include directory list - list(APPEND GINKGO_INTERFACE_CFLAGS_FOUND "-I\\\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + list(APPEND GINKGO_INTERFACE_CFLAGS_FOUND "-std=c++${GINKGO_CXX_STANDARD}" "-I\\\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") # Call the recursive interface libraries macro get_target_property(GINKGO_INTERFACE_LINK_LIBRARIES ginkgo INTERFACE_LINK_LIBRARIES) diff --git a/core/test/accessor/CMakeLists.txt b/core/test/accessor/CMakeLists.txt index 4fd0ff158d0..dbc64a5e8da 100644 --- a/core/test/accessor/CMakeLists.txt +++ b/core/test/accessor/CMakeLists.txt @@ -6,7 +6,7 @@ function(create_accessor_test test_name) ${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) string(REPLACE "/" "_" TEST_TARGET_NAME "${REL_BINARY_DIR}/${test_name}") add_executable("${TEST_TARGET_NAME}" "${test_name}.cpp") - target_compile_features("${TEST_TARGET_NAME}" PUBLIC cxx_std_14) + target_compile_features("${TEST_TARGET_NAME}" PUBLIC ${GINKGO_CXX_STANDARD_FEATURE}) target_include_directories("${TEST_TARGET_NAME}" PRIVATE "${Ginkgo_SOURCE_DIR}" diff --git a/core/test/gtest/ginkgo_mpi_main.cpp b/core/test/gtest/ginkgo_mpi_main.cpp index a94cdc710a5..7e212c4d3f6 100644 --- a/core/test/gtest/ginkgo_mpi_main.cpp +++ b/core/test/gtest/ginkgo_mpi_main.cpp @@ -360,7 +360,13 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - MPI_Init(&argc, &argv); + int provided_thread_support; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, + &provided_thread_support); + if (provided_thread_support != MPI_THREAD_MULTIPLE) { + throw std::runtime_error( + "This test requires an thread compliant MPI implementation."); + } MPI_Comm comm(MPI_COMM_WORLD); int rank; int size; diff --git a/core/test/mpi/distributed/CMakeLists.txt b/core/test/mpi/distributed/CMakeLists.txt index db3be5e8e39..ad6929ecdef 100644 --- a/core/test/mpi/distributed/CMakeLists.txt +++ b/core/test/mpi/distributed/CMakeLists.txt @@ -1,5 +1,6 @@ ginkgo_create_test(helpers MPI_SIZE 1) ginkgo_create_test(matrix MPI_SIZE 1) ginkgo_create_test(neighborhood_communicator MPI_SIZE 6) +ginkgo_create_test(row_gatherer MPI_SIZE 6) add_subdirectory(preconditioner) diff --git a/core/test/mpi/distributed/row_gatherer.cpp b/core/test/mpi/distributed/row_gatherer.cpp new file mode 100644 index 00000000000..c39a8035c76 --- /dev/null +++ b/core/test/mpi/distributed/row_gatherer.cpp @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// +// SPDX-License-Identifier: BSD-3-Clause + +#include + + +#include +#include +#include + + +#include "core/test/utils.hpp" +#include "core/test/utils/assertions.hpp" + + +template +class RowGatherer : public ::testing::Test { +protected: + using index_type = IndexType; + using part_type = + gko::experimental::distributed::Partition; + using map_type = + gko::experimental::distributed::index_map; + using row_gatherer_type = + gko::experimental::distributed::RowGatherer; + + RowGatherer() + { + int rank = this->comm.rank(); + auto part = gko::share(part_type::build_from_global_size_uniform( + this->ref, this->comm.size(), this->comm.size() * 3)); + auto recv_connections = + this->template create_recv_connections()[rank]; + auto imap = + map_type{this->ref, part, this->comm.rank(), recv_connections}; + auto coll_comm = + std::make_shared( + this->comm, imap); + rg = row_gatherer_type::create(ref, coll_comm, imap); + } + + void SetUp() override { ASSERT_EQ(comm.size(), 6); } + + template + std::array, 6> create_recv_connections() + { + return {gko::array{ref, {3, 5, 10, 11}}, + gko::array{ref, {0, 1, 7, 12, 13}}, + gko::array{ref, {3, 4, 17}}, + gko::array{ref, {1, 2, 12, 14}}, + gko::array{ref, {4, 5, 9, 10, 15, 16}}, + gko::array{ref, {8, 12, 13, 14}}}; + } + + std::shared_ptr ref = gko::ReferenceExecutor::create(); + gko::experimental::mpi::communicator comm = MPI_COMM_WORLD; + std::shared_ptr rg; +}; + +TYPED_TEST_SUITE(RowGatherer, gko::test::IndexTypes, TypenameNameGenerator); + +TYPED_TEST(RowGatherer, CanDefaultConstruct) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + + auto rg = RowGatherer::create(this->ref, this->comm); + + GKO_ASSERT_EQUAL_DIMENSIONS(rg, gko::dim<2>()); +} + + +TYPED_TEST(RowGatherer, CanConstructWithEmptCollectiveCommAndIndexMap) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using IndexMap = typename TestFixture::map_type; + auto coll_comm = + std::make_shared( + this->comm); + auto map = IndexMap{this->ref}; + + auto rg = RowGatherer::create(this->ref, coll_comm, map); + + GKO_ASSERT_EQUAL_DIMENSIONS(rg, gko::dim<2>()); +} + + +TYPED_TEST(RowGatherer, CanConstructFromCollectiveCommAndIndexMap) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using Part = typename TestFixture::part_type; + using IndexMap = typename TestFixture::map_type; + int rank = this->comm.rank(); + auto part = gko::share(Part::build_from_global_size_uniform( + this->ref, this->comm.size(), this->comm.size() * 3)); + auto recv_connections = + this->template create_recv_connections()[rank]; + auto imap = IndexMap{this->ref, part, this->comm.rank(), recv_connections}; + auto coll_comm = + std::make_shared( + this->comm, imap); + + auto rg = RowGatherer::create(this->ref, coll_comm, imap); + + gko::dim<2> size{recv_connections.get_size(), 18}; + GKO_ASSERT_EQUAL_DIMENSIONS(rg, size); +} + + +TYPED_TEST(RowGatherer, CanApply) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + this->rg->apply(b, x); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsync) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + auto future = this->rg->apply_async(b, x); + future.wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanFinishFutureAfterReset) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + auto future = this->rg->apply_async(b, x); + this->rg.reset(); + future.wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsyncWithMultipleColumns) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 2}, + gko::initialize({{offset, offset * offset}, + {offset + 1, offset * offset + 1}, + {offset + 2, offset * offset + 2}}, + this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 2}); + + this->rg->apply_async(b, x).wait(); + + gko::array expected[] = { + gko::array{this->ref, {3, 9, 5, 11, 10, 82, 11, 83}}, + gko::array{this->ref, {0, 0, 1, 1, 7, 37, 12, 144, 13, 145}}, + gko::array{this->ref, {3, 9, 4, 10, 17, 227}}, + gko::array{this->ref, {1, 1, 2, 2, 12, 144, 14, 146}}, + gko::array{this->ref, + {4, 10, 5, 11, 9, 81, 10, 82, 15, 225, 16, 226}}, + gko::array{this->ref, {8, 38, 12, 144, 13, 145, 14, 146}}}; + auto expected_vec = + Dense::create(this->ref, gko::dim<2>{expected[rank].get_size() / 2, 2}, + expected[rank], 2); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, ThrowsOnAdvancedApply) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + auto rg = RowGatherer::create(this->ref, this->comm); + auto b = Vector::create(this->ref, this->comm); + auto x = Dense::create(this->ref); + auto alpha = Dense::create(this->ref, gko::dim<2>{1, 1}); + auto beta = Dense::create(this->ref, gko::dim<2>{1, 1}); + + ASSERT_THROW(rg->apply(alpha, b, beta, x), gko::NotImplemented); +} diff --git a/dpcpp/CMakeLists.txt b/dpcpp/CMakeLists.txt index 6c75943b330..9ebecf54ab4 100644 --- a/dpcpp/CMakeLists.txt +++ b/dpcpp/CMakeLists.txt @@ -102,7 +102,7 @@ gko_add_sycl_to_target(TARGET ginkgo_dpcpp) # Note: add MKL as PRIVATE not PUBLIC (MKL example shows) to avoid propagating # find_package(MKL) everywhere when linking ginkgo (see the MKL example # https://software.intel.com/content/www/us/en/develop/documentation/onemkl-windows-developer-guide/top/getting-started/cmake-config-for-onemkl.html) -target_compile_features(ginkgo_dpcpp PUBLIC cxx_std_17) +target_compile_features(ginkgo_dpcpp PRIVATE cxx_std_17 INTERFACE ${GINKGO_CXX_STANDARD_FEATURE}) target_link_options(ginkgo_dpcpp PRIVATE -fsycl-device-lib=all) # When building ginkgo as a static library, we need to use dpcpp and per_kernel # link option when the program uses a dpcpp related function. diff --git a/include/ginkgo/config.hpp.in b/include/ginkgo/config.hpp.in index 329918399d6..98c56472bb4 100644 --- a/include/ginkgo/config.hpp.in +++ b/include/ginkgo/config.hpp.in @@ -112,4 +112,10 @@ // clang-format on +/* Do we support C++17? */ +// clang-format off +#cmakedefine01 GINKGO_HAVE_CXX17 +// clang-format on + + #endif // GKO_INCLUDE_CONFIG_H diff --git a/test/test_exportbuild/CMakeLists.txt b/test/test_exportbuild/CMakeLists.txt index c65f964d9a0..cb8f0b1225c 100644 --- a/test/test_exportbuild/CMakeLists.txt +++ b/test/test_exportbuild/CMakeLists.txt @@ -7,5 +7,4 @@ find_package(Ginkgo REQUIRED # Here, we use test install without any data. We instantiate the # interface only. add_executable(test_exportbuild ../test_install/test_install.cpp) -target_compile_features(test_exportbuild PUBLIC cxx_std_14) target_link_libraries(test_exportbuild PRIVATE Ginkgo::ginkgo) diff --git a/test/test_install/CMakeLists.txt b/test/test_install/CMakeLists.txt index ee19b8d030e..cf5628df88a 100644 --- a/test/test_install/CMakeLists.txt +++ b/test/test_install/CMakeLists.txt @@ -26,7 +26,6 @@ if (GINKGO_BUILD_REFERENCE) set(HAS_REFERENCE 1) endif() add_executable(test_install test_install.cpp) -target_compile_features(test_install PUBLIC cxx_std_14) target_compile_definitions(test_install PRIVATE HAS_REFERENCE=${HAS_REFERENCE}) target_link_libraries(test_install PRIVATE Ginkgo::ginkgo) if(GINKGO_BUILD_MPI) diff --git a/test/test_pkgconfig/CMakeLists.txt b/test/test_pkgconfig/CMakeLists.txt index e904f997f26..ad78dfc9ed2 100644 --- a/test/test_pkgconfig/CMakeLists.txt +++ b/test/test_pkgconfig/CMakeLists.txt @@ -8,7 +8,6 @@ pkg_check_modules(GINKGO REQUIRED IMPORTED_TARGET ginkgo) # Here, we use test install without any data. We instantiate the # interface only. add_executable(test_pkgconfig ../test_install/test_install.cpp) -target_compile_features(test_pkgconfig PUBLIC cxx_std_14) # CMake PkgConfig only puts the -l, -L, and -framework into link_libraries and others into link_options # When linking the target, the linking option will be before the compiled object to lead the linking error set_property(TARGET PkgConfig::GINKGO PROPERTY INTERFACE_LINK_LIBRARIES "${GINKGO_LDFLAGS}") diff --git a/test/test_subdir/CMakeLists.txt b/test/test_subdir/CMakeLists.txt index dcf846f4adc..00ae3bc07e2 100644 --- a/test/test_subdir/CMakeLists.txt +++ b/test/test_subdir/CMakeLists.txt @@ -5,5 +5,4 @@ file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/../.." "${CMAKE_CURRENT_BINARY_DIR add_subdirectory("${CMAKE_CURRENT_BINARY_DIR}/ginkgo") add_executable(test_subdir ../test_install/test_install.cpp) -target_compile_features(test_subdir PUBLIC cxx_std_14) target_link_libraries(test_subdir PRIVATE Ginkgo::ginkgo)