diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 69c7891403..18f95f652b 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -39,6 +39,7 @@ rapids_cmake_build_type(Release) # * build options ---------------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build KvikIO shared library" ON) +option(KvikIO_BUILD_BENCHMARKS "Configure CMake to build benchmarks" ON) option(KvikIO_BUILD_EXAMPLES "Configure CMake to build examples" ON) option(KvikIO_BUILD_TESTS "Configure CMake to build tests" ON) option(KvikIO_REMOTE_SUPPORT "Configure CMake to build with remote IO support" ON) @@ -203,6 +204,17 @@ set_target_properties( INTERFACE_POSITION_INDEPENDENT_CODE ON ) +# ################################################################################################## +# * add benchmarks -------------------------------------------------------------------------------- + +if(KvikIO_BUILD_BENCHMARKS) + # Find or install GoogleBench + include(${rapids-cmake-dir}/cpm/gbench.cmake) + rapids_cpm_gbench(BUILD_STATIC) + + add_subdirectory(benchmarks) +endif() + # ################################################################################################## # * add examples ----------------------------------------------------------------------------------- diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt new file mode 100644 index 0000000000..665d45edfb --- /dev/null +++ b/cpp/benchmarks/CMakeLists.txt @@ -0,0 +1,62 @@ +# ============================================================================= +# Copyright (c) 2025, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +# in compliance with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing permissions and limitations under +# the License. +# ============================================================================= + +#[=======================================================================[.rst: +kvikio_add_benchmark +-------------------- + +Create a KvikIO benchmark. + +.. code-block:: cmake + + kvikio_add_benchmark(NAME SOURCES ) + + ``NAME`` + Benchmark name. Single-value argument. + + ``SOURCES`` + List of source files for the benchmark. Multi-value argument. +#]=======================================================================] +function(kvikio_add_benchmark) + cmake_parse_arguments( + _KVIKIO # prefix + "" # optional + "NAME" # single value + "SOURCES" # multi-value + ${ARGN} + ) + + if(DEFINED _KVIKIO_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown argument: ${_KVIKIO_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${_KVIKIO_NAME} ${_KVIKIO_SOURCES}) + set_target_properties(${_KVIKIO_NAME} PROPERTIES INSTALL_RPATH "\$ORIGIN/../../../lib") + + target_link_libraries(${_KVIKIO_NAME} PUBLIC benchmark::benchmark kvikio::kvikio) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas") + target_compile_options(${_KVIKIO_NAME} PRIVATE "$<$:${KVIKIO_CXX_FLAGS}>") + endif() + + install( + TARGETS ${_KVIKIO_NAME} + COMPONENT testing + DESTINATION bin/benchmarks/libkvikio + EXCLUDE_FROM_ALL + ) +endfunction() + +kvikio_add_benchmark(NAME THREADPOOL_BENCHMARK SOURCES "threadpool/threadpool_benchmark.cpp") diff --git a/cpp/benchmarks/threadpool/threadpool_benchmark.cpp b/cpp/benchmarks/threadpool/threadpool_benchmark.cpp new file mode 100644 index 0000000000..1c90579d22 --- /dev/null +++ b/cpp/benchmarks/threadpool/threadpool_benchmark.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This benchmark assesses the scalability of the thread pool. +// +// In the "strong scaling" study, the total amount of tasks is fixed, and the time to complete +// these tasks is evaluated as a function of thread count. +// +// In the "weak scaling" study, the expected amount of tasks per thread is fixed, and the total +// amount of tasks is then proportional to the thread count. Again, the time is evaluated as a +// function of thread count. + +#include +#include + +#include +#include + +namespace kvikio { +enum class ScalingType : uint8_t { + STRONG_SCALING, + WEAK_SCALING, +}; + +void task_compute(std::size_t num_compute_iterations) +{ + [[maybe_unused]] double res{0.0}; + for (std::size_t i = 0u; i < num_compute_iterations; ++i) { + auto x{static_cast(i)}; + benchmark::DoNotOptimize(res += std::sqrt(x) + std::cbrt(x) + std::sin(x)); + } +} + +template +void BM_threadpool_compute(benchmark::State& state) +{ + auto const num_threads = state.range(0); + + std::size_t const num_compute_tasks = + (scaling_type == ScalingType::STRONG_SCALING) ? 10'000 : (1'000 * num_threads); + + std::size_t constexpr num_compute_iterations{1'000}; + kvikio::defaults::set_thread_pool_nthreads(num_threads); + + for (auto _ : state) { + // Submit a total of "num_compute_tasks" tasks to the thread pool. + for (auto i = std::size_t{0}; i < num_compute_tasks; ++i) { + [[maybe_unused]] auto fut = + kvikio::defaults::thread_pool().submit_task([] { task_compute(num_compute_iterations); }); + } + kvikio::defaults::thread_pool().wait(); + } + + state.counters["threads"] = num_threads; +} +} // namespace kvikio + +int main(int argc, char** argv) +{ + benchmark::Initialize(&argc, argv); + + benchmark::RegisterBenchmark("BM_threadpool_compute:strong_scaling", + kvikio::BM_threadpool_compute) + ->RangeMultiplier(2) + ->Range(1, 64) // Increase from 1 to 64 (inclusive of both endpoints) with x2 stepping. + ->UseRealTime() // Use the wall clock to determine the number of benchmark iterations. + ->Unit(benchmark::kMillisecond) + ->MinTime(2); // Minimum of 2 seconds. + + benchmark::RegisterBenchmark("BM_threadpool_compute:weak_scaling", + kvikio::BM_threadpool_compute) + ->RangeMultiplier(2) + ->Range(1, 64) + ->UseRealTime() + ->Unit(benchmark::kMillisecond) + ->MinTime(2); + + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); +} diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 6b03b1d487..b7aa73dd2d 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -19,10 +19,9 @@ set(TEST_INSTALL_PATH bin/tests/libkvikio) if(CUDAToolkit_FOUND) add_executable(BASIC_IO_EXAMPLE basic_io.cpp) set_target_properties(BASIC_IO_EXAMPLE PROPERTIES INSTALL_RPATH "\$ORIGIN/../../../lib") - target_include_directories(BASIC_IO_EXAMPLE PRIVATE ../include ${cuFile_INCLUDE_DIRS}) - target_link_libraries(BASIC_IO_EXAMPLE PRIVATE kvikio CUDA::cudart) + target_link_libraries(BASIC_IO_EXAMPLE PRIVATE kvikio::kvikio CUDA::cudart) - if(CMAKE_COMPILER_IS_GNUCXX) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas") target_compile_options( BASIC_IO_EXAMPLE PRIVATE "$<$:${KVIKIO_CXX_FLAGS}>" @@ -43,10 +42,9 @@ endif() add_executable(BASIC_NO_CUDA_EXAMPLE basic_no_cuda.cpp) set_target_properties(BASIC_NO_CUDA_EXAMPLE PROPERTIES INSTALL_RPATH "\$ORIGIN/../../../lib") -target_include_directories(BASIC_NO_CUDA_EXAMPLE PRIVATE ../include) -target_link_libraries(BASIC_NO_CUDA_EXAMPLE PRIVATE kvikio) +target_link_libraries(BASIC_NO_CUDA_EXAMPLE PRIVATE kvikio::kvikio) -if(CMAKE_COMPILER_IS_GNUCXX) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas") target_compile_options( BASIC_NO_CUDA_EXAMPLE PRIVATE "$<$:${KVIKIO_CXX_FLAGS}>" diff --git a/python/libkvikio/CMakeLists.txt b/python/libkvikio/CMakeLists.txt index 806efd2b1c..f159cffa1a 100644 --- a/python/libkvikio/CMakeLists.txt +++ b/python/libkvikio/CMakeLists.txt @@ -36,6 +36,7 @@ endif() unset(kvikio_FOUND) +set(KvikIO_BUILD_BENCHMARKS OFF) set(KvikIO_BUILD_EXAMPLES OFF) set(KvikIO_BUILD_TESTS OFF) if(USE_NVCOMP_RUNTIME_WHEEL)