diff --git a/ci/check_style.sh b/ci/check_style.sh index 682d00e142c6..785217419e3c 100755 --- a/ci/check_style.sh +++ b/ci/check_style.sh @@ -1,5 +1,5 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -euo pipefail @@ -28,4 +28,5 @@ mkdir -p "$(dirname "${RAPIDS_CMAKE_FORMAT_FILE}")" wget -O ${RAPIDS_CMAKE_FORMAT_FILE} "${FORMAT_FILE_URL}" # Run pre-commit checks +pre-commit clean pre-commit run --all-files --show-diff-on-failure diff --git a/conda/recipes/libcudf/recipe.yaml b/conda/recipes/libcudf/recipe.yaml index 060917b7ad6c..ecdb400b900a 100644 --- a/conda/recipes/libcudf/recipe.yaml +++ b/conda/recipes/libcudf/recipe.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 schema_version: 1 @@ -437,7 +437,7 @@ outputs: - libnvcomp tests: - script: - - test -f $PREFIX/bin/gtests/libcudf_streaming/cudf_streaming_single_tests + - test -f $PREFIX/bin/gtests/libcudf_streaming/libcudf_streaming_single_tests - test -f $PREFIX/bin/libcudf_streaming_bench_pack - test -f $PREFIX/bin/libcudf_streaming_bench_partition - test -f $PREFIX/bin/libcudf_streaming_bench_ndsh_bench_read diff --git a/cpp/libcudf_streaming/cmake/ConfigureOptionalCommunication.cmake b/cpp/libcudf_streaming/cmake/ConfigureOptionalCommunication.cmake index 1122e9618580..546a556616b0 100644 --- a/cpp/libcudf_streaming/cmake/ConfigureOptionalCommunication.cmake +++ b/cpp/libcudf_streaming/cmake/ConfigureOptionalCommunication.cmake @@ -1,6 +1,6 @@ # ============================================================================= # cmake-format: off -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # cmake-format: on # ============================================================================= @@ -12,7 +12,10 @@ set(CUDF_STREAMING_FOUND_UCXX OFF) set(CUDF_STREAMING_HAVE_MPI OFF) set(CUDF_STREAMING_HAVE_UCXX OFF) -if(BUILD_BENCHMARKS OR BUILD_EXAMPLES) +if(BUILD_BENCHMARKS + OR BUILD_EXAMPLES + OR BUILD_TESTS +) rapids_find_package( MPI QUIET BUILD_EXPORT_SET cudf_streaming-exports @@ -23,7 +26,7 @@ if(BUILD_BENCHMARKS OR BUILD_EXAMPLES) endif() endif() -if(BUILD_BENCHMARKS) +if(BUILD_BENCHMARKS OR BUILD_TESTS) rapids_find_package( ucxx QUIET BUILD_EXPORT_SET cudf_streaming-exports diff --git a/cpp/libcudf_streaming/cmake/thirdparty/get_cudf.cmake b/cpp/libcudf_streaming/cmake/thirdparty/get_cudf.cmake index d8c816fe1eb7..dc147d39cfda 100644 --- a/cpp/libcudf_streaming/cmake/thirdparty/get_cudf.cmake +++ b/cpp/libcudf_streaming/cmake/thirdparty/get_cudf.cmake @@ -1,6 +1,6 @@ # ============================================================================= # cmake-format: off -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # cmake-format: on # ============================================================================= @@ -12,6 +12,20 @@ set(CUDF_STREAMING_MIN_VERSION ) find_and_configure_cudf(${CUDF_STREAMING_MIN_VERSION} cudf_streaming-exports) +# The cudf-dependent tests link against cudf::cudftestutil{,_impl}, which live in cudf's optional +# `testing` component. The transitive find_dependency(cudf) triggered by find_and_configure_cudf +# does not request it, so request it explicitly here when building tests. This avoids having to +# build cudf from source: the component is provided by the installed (e.g. conda) cudf package. +if(BUILD_TESTS AND NOT TARGET cudf::cudftestutil) + find_package(cudf ${CUDF_STREAMING_MIN_VERSION} REQUIRED COMPONENTS testing) + + foreach(_target cudf::cudftestutil cudf::cudftestutil_impl) + if(NOT TARGET ${_target}) + message(WARNING "cudf testing target NOT available: ${_target}") + endif() + endforeach() +endif() + if(cudf_REQUIRES_CUDA) rapids_cuda_init_architectures(CUDF_STREAMING) diff --git a/cpp/libcudf_streaming/tests/CMakeLists.txt b/cpp/libcudf_streaming/tests/CMakeLists.txt index 76bcbea20839..1e9f8371f17b 100644 --- a/cpp/libcudf_streaming/tests/CMakeLists.txt +++ b/cpp/libcudf_streaming/tests/CMakeLists.txt @@ -1,6 +1,6 @@ # ============================================================================= # cmake-format: off -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # cmake-format: on # ============================================================================= @@ -20,9 +20,36 @@ if(NOT EXISTS "${_cudf_streaming_gtests_link}") file(CREATE_LINK "${CUDF_STREAMING_BINARY_DIR}/gtests" "${_cudf_streaming_gtests_link}" SYMBOLIC) endif() -add_library(cudf_streaming_test_sources OBJECT) +# Create a separate test case for each of these n_ranks. +set(nranks_to_run 1 2 5) + +# A helper function to create a test case for each parallelism (defined by nranks_to_run). +# TEST_TARGET is the name of the test target (required). This adds len(nranks_to_run) test cases +# identified by _. NOTE: When running ctest, the TEST_TARGET executable should +# be located in the ./gtests/ directory relative to the PWD. +function(libcudf_streaming_mpirun_test_add) + set(options) # no options + set(one_value TEST_TARGET) + set(multi_value) # no multi_value args + cmake_parse_arguments(_MPIRUN_TEST "${options}" "${one_value}" "${multi_value}" ${ARGN}) + + if(NOT DEFINED _MPIRUN_TEST_TEST_TARGET) + message(FATAL_ERROR "libcudf_streaming_mpirun_test_add called without a test prefix") + endif() + + message(STATUS "Adding mpirun test: ${_MPIRUN_TEST_TEST_TARGET} nranks: ${nranks_to_run}") + foreach(np IN ITEMS ${nranks_to_run}) + # add the test using the relative path of the target + add_test( + NAME "${_MPIRUN_TEST_TEST_TARGET}_${np}" COMMAND mpirun --map-by node --bind-to none -np + ${np} "gtests/${_MPIRUN_TEST_TEST_TARGET}" + ) + endforeach(np) +endfunction(libcudf_streaming_mpirun_test_add) + +add_library(libcudf_streaming_test_sources OBJECT) set_target_properties( - cudf_streaming_test_sources + libcudf_streaming_test_sources PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON @@ -30,20 +57,26 @@ set_target_properties( CUDA_STANDARD_REQUIRED ON ) target_include_directories( - cudf_streaming_test_sources PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}" + libcudf_streaming_test_sources PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) target_compile_options( - cudf_streaming_test_sources + libcudf_streaming_test_sources PRIVATE $<$:${CUDF_CUDA_FLAGS};--expt-extended-lambda> ) target_link_libraries( - cudf_streaming_test_sources + libcudf_streaming_test_sources PRIVATE cudf_streaming rapidsmpf::rapidsmpf cudf::cudftestutil cudf::cudftestutil_impl PUBLIC GTest::gmock GTest::gtest ) +# cudf::cudftestutil_impl injects cudf test-utility .cu sources (via INTERFACE_SOURCES) that are +# compiled into this object library. Those sources use device lambdas, so they require the same CUDA +# flags cudf builds them with (notably --expt-extended-lambda from CUDF_CUDA_FLAGS). +target_compile_options( + libcudf_streaming_test_sources PRIVATE "$<$:${CUDF_CUDA_FLAGS}>" +) target_sources( - cudf_streaming_test_sources + libcudf_streaming_test_sources PRIVATE streaming/test_table_chunk.cpp streaming/test_read_parquet.cpp streaming/test_channel_metadata.cpp @@ -55,9 +88,9 @@ target_sources( test_shuffler.cpp ) -add_executable(cudf_streaming_single_tests main/single.cpp) +add_executable(libcudf_streaming_single_tests main/single.cpp) set_target_properties( - cudf_streaming_single_tests + libcudf_streaming_single_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CUDF_STREAMING_BINARY_DIR}/gtests" INSTALL_RPATH "\$ORIGIN/../../../lib" CXX_STANDARD 20 @@ -67,22 +100,84 @@ set_target_properties( CUDA_STANDARD_REQUIRED ON ) target_include_directories( - cudf_streaming_single_tests PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}" + libcudf_streaming_single_tests PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) target_link_libraries( - cudf_streaming_single_tests + libcudf_streaming_single_tests PRIVATE cudf_streaming rapidsmpf::rapidsmpf GTest::gmock GTest::gtest - $ cudf_streaming_test_sources + $ libcudf_streaming_test_sources ) rapids_test_add( - NAME cudf_streaming_single_tests - COMMAND cudf_streaming_single_tests + NAME libcudf_streaming_single_tests + COMMAND libcudf_streaming_single_tests GPUS 1 PERCENT 100 INSTALL_COMPONENT_SET testing ) +# ################################################################################################## +# MPI and UCXX based tests ------------------------------------------------------------------------- +# These executables reuse libcudf_streaming_test_sources but provide a different test environment +# main (main/mpi.cpp / main/ucxx.cpp), and are launched via mpirun for multiple ranks. +if(CUDF_STREAMING_HAVE_MPI) + add_executable(libcudf_streaming_mpi_tests main/mpi.cpp) + set_target_properties( + libcudf_streaming_mpi_tests + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CUDF_STREAMING_BINARY_DIR}/gtests" + INSTALL_RPATH "\$ORIGIN/../../../lib" + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + CUDA_STANDARD 20 + CUDA_STANDARD_REQUIRED ON + ) + target_include_directories( + libcudf_streaming_mpi_tests PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + target_link_libraries( + libcudf_streaming_mpi_tests + PRIVATE cudf_streaming rapidsmpf::rapidsmpf cudf_streaming::optional_communication GTest::gmock + GTest::gtest $ libcudf_streaming_test_sources + ) + libcudf_streaming_mpirun_test_add(TEST_TARGET libcudf_streaming_mpi_tests) + + if(CUDF_STREAMING_HAVE_UCXX) + add_executable(libcudf_streaming_ucxx_tests main/ucxx.cpp) + set_target_properties( + libcudf_streaming_ucxx_tests + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CUDF_STREAMING_BINARY_DIR}/gtests" + INSTALL_RPATH "\$ORIGIN/../../../lib" + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + CUDA_STANDARD 20 + CUDA_STANDARD_REQUIRED ON + ) + target_include_directories( + libcudf_streaming_ucxx_tests PRIVATE "${CUDF_STREAMING_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + target_link_libraries( + libcudf_streaming_ucxx_tests + PRIVATE cudf_streaming + rapidsmpf::rapidsmpf + cudf_streaming::optional_communication + ucxx::ucxx + GTest::gmock + GTest::gtest + $ + libcudf_streaming_test_sources + ) + libcudf_streaming_mpirun_test_add(TEST_TARGET libcudf_streaming_ucxx_tests) + else() + cudf_streaming_skip_optional_target(libcudf_streaming_ucxx_tests "UCXX not found") + endif() +else() + cudf_streaming_skip_optional_target(libcudf_streaming_mpi_tests "MPI not found") +endif() + rapids_test_install_relocatable( INSTALL_COMPONENT_SET testing DESTINATION bin/gtests/libcudf_streaming ) diff --git a/cpp/libcudf_streaming/tests/environment.hpp b/cpp/libcudf_streaming/tests/environment.hpp index 648d010ea11b..2a16d482fc0c 100644 --- a/cpp/libcudf_streaming/tests/environment.hpp +++ b/cpp/libcudf_streaming/tests/environment.hpp @@ -1,37 +1,50 @@ -/** - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights - * reserved. SPDX-License-Identifier: Apache-2.0 +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 */ + #pragma once #include #include +#include + enum class TestEnvironmentType : int { + MPI, + UCXX, SINGLE, }; +/** + * Base test environment that exposes a communicator to the test suite. Concrete, + * communicator-specific environments (single/MPI/UCXX) derive from this class and are defined in + * the corresponding file in main dir, where the communicator-specific state (e.g. the MPI + * communicator) and headers live. Exactly one of them is linked into each test executable. + */ class Environment : public ::testing::Environment { public: - Environment(int argc, char** argv); + Environment(int argc, char** argv) : argc_(argc), argv_(argv) {} + ~Environment() override = default; - void SetUp() override; + void SetUp() override = 0; - void TearDown() override; + void TearDown() override = 0; - void barrier(); + virtual void barrier() = 0; - [[nodiscard]] TestEnvironmentType type() const; + [[nodiscard]] virtual TestEnvironmentType type() const = 0; - constexpr rapidsmpf::config::Options& options() { return options_; } + rapidsmpf::config::Options& options() { return options_; } - std::shared_ptr split_comm(); + virtual std::shared_ptr split_comm() = 0; std::shared_ptr comm_; - private: - std::shared_ptr split_comm_{nullptr}; + protected: + int argc_; + char** argv_; rapidsmpf::config::Options options_; }; diff --git a/cpp/libcudf_streaming/tests/main/mpi.cpp b/cpp/libcudf_streaming/tests/main/mpi.cpp new file mode 100644 index 000000000000..ecfc4ce1cb12 --- /dev/null +++ b/cpp/libcudf_streaming/tests/main/mpi.cpp @@ -0,0 +1,78 @@ +/** + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * reserved. SPDX-License-Identifier: Apache-2.0 + */ + +#include "../environment.hpp" + +#include + +#include +#include +#include +#include + +#include + +namespace { +class MPIEnvironment : public Environment { + public: + using Environment::Environment; + + [[nodiscard]] TestEnvironmentType type() const override { return TestEnvironmentType::MPI; } + + void SetUp() override + { + rapidsmpf::mpi::init(&argc_, &argv_); + + RAPIDSMPF_MPI(MPI_Comm_dup(MPI_COMM_WORLD, &mpi_comm_)); + + options_ = rapidsmpf::config::Options(rapidsmpf::config::get_environment_variables()); + + comm_ = std::make_shared( + mpi_comm_, options_, std::make_shared()); + } + + void TearDown() override + { + comm_ = nullptr; // Clean up the communicator. + + RAPIDSMPF_MPI(MPI_Comm_free(&mpi_comm_)); + RAPIDSMPF_MPI(MPI_Finalize()); + } + + void barrier() override { RAPIDSMPF_MPI(MPI_Barrier(mpi_comm_)); } + + std::shared_ptr split_comm() override + { + // Initialize configuration options from environment variables. + rapidsmpf::config::Options options{rapidsmpf::config::get_environment_variables()}; + + // Create the new split communicator + int rank; + RAPIDSMPF_MPI(MPI_Comm_rank(mpi_comm_, &rank)); + MPI_Comm split_comm = MPI_COMM_NULL; + RAPIDSMPF_MPI(MPI_Comm_split(mpi_comm_, rank, 0, &split_comm)); + return std::shared_ptr( + new rapidsmpf::MPI(split_comm, options, comm_->progress_thread()), + // Don't leak the split handle. + [comm = split_comm](rapidsmpf::MPI* x) mutable { + delete x; + MPI_Comm_free(&comm); + }); + } + + private: + MPI_Comm mpi_comm_{MPI_COMM_NULL}; +}; +} // namespace + +Environment* GlobalEnvironment = nullptr; + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + GlobalEnvironment = new MPIEnvironment(argc, argv); + ::testing::AddGlobalTestEnvironment(GlobalEnvironment); + return RUN_ALL_TESTS(); +} diff --git a/cpp/libcudf_streaming/tests/main/single.cpp b/cpp/libcudf_streaming/tests/main/single.cpp index 22a4a9286790..cf54b83eca5a 100644 --- a/cpp/libcudf_streaming/tests/main/single.cpp +++ b/cpp/libcudf_streaming/tests/main/single.cpp @@ -1,6 +1,6 @@ -/** - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights - * reserved. SPDX-License-Identifier: Apache-2.0 +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "../environment.hpp" @@ -8,38 +8,39 @@ #include #include +#include #include #include -Environment* GlobalEnvironment = nullptr; +namespace { +class SingleEnvironment : public Environment { + public: + using Environment::Environment; -Environment::Environment(int, char**) {} + [[nodiscard]] TestEnvironmentType type() const override { return TestEnvironmentType::SINGLE; } -TestEnvironmentType Environment::type() const { return TestEnvironmentType::SINGLE; } + void SetUp() override + { + options_ = rapidsmpf::config::Options(rapidsmpf::config::get_environment_variables()); + comm_ = + std::make_shared(options_, std::make_shared()); + } -void Environment::SetUp() -{ - options_ = rapidsmpf::config::Options(rapidsmpf::config::get_environment_variables()); - comm_ = - std::make_shared(options_, std::make_shared()); - split_comm_ = comm_; -} + void TearDown() override { comm_ = nullptr; } -void Environment::TearDown() -{ - split_comm_ = nullptr; - comm_ = nullptr; -} + void barrier() override {} -void Environment::barrier() {} + std::shared_ptr split_comm() override { return comm_; } +}; +} // namespace -std::shared_ptr Environment::split_comm() { return split_comm_; } +Environment* GlobalEnvironment = nullptr; int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - GlobalEnvironment = new Environment(argc, argv); + GlobalEnvironment = new SingleEnvironment(argc, argv); ::testing::AddGlobalTestEnvironment(GlobalEnvironment); return RUN_ALL_TESTS(); } diff --git a/cpp/libcudf_streaming/tests/main/ucxx.cpp b/cpp/libcudf_streaming/tests/main/ucxx.cpp new file mode 100644 index 000000000000..e6179752a7f4 --- /dev/null +++ b/cpp/libcudf_streaming/tests/main/ucxx.cpp @@ -0,0 +1,71 @@ +/** + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * reserved. SPDX-License-Identifier: Apache-2.0 + */ + +#include "../environment.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { +class UCXXEnvironment : public Environment { + public: + using Environment::Environment; + + [[nodiscard]] TestEnvironmentType type() const override { return TestEnvironmentType::UCXX; } + + void SetUp() override + { + // Ensure CUDA context is created before UCX is initialized. + RAPIDSMPF_CUDA_TRY(cudaFree(nullptr)); + + // Explicitly initialize MPI. We can not use rapidsmpf::mpi::init as it checks some + // rapidsmpf::MPI communicator specific conditions + int provided; + RAPIDSMPF_MPI(MPI_Init_thread(&argc_, &argv_, MPI_THREAD_MULTIPLE, &provided)); + RAPIDSMPF_EXPECTS(provided == MPI_THREAD_MULTIPLE, + "didn't get the requested thread level support: MPI_THREAD_MULTIPLE"); + + options_ = rapidsmpf::config::Options(rapidsmpf::config::get_environment_variables()); + comm_ = rapidsmpf::ucxx::init_using_mpi( + MPI_COMM_WORLD, options_, std::make_shared()); + } + + void TearDown() override + { + // Ensure UCXX cleanup before MPI. If this is not done failures related to + // accessing the CUDA context may be thrown during shutdown. + comm_ = nullptr; // Clean up the communicator. + RAPIDSMPF_MPI(MPI_Finalize()); + } + + void barrier() override { std::dynamic_pointer_cast(comm_)->barrier(); } + + std::shared_ptr split_comm() override + { + return std::dynamic_pointer_cast(comm_)->split(); + } +}; +} // namespace + +Environment* GlobalEnvironment = nullptr; + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + GlobalEnvironment = new UCXXEnvironment(argc, argv); + ::testing::AddGlobalTestEnvironment(GlobalEnvironment); + return RUN_ALL_TESTS(); +}