diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 000000000..9ec6f8f33 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,73 @@ +name: benchmarks +on: + workflow_dispatch: + pull_request: + push: + branches: [master] +concurrency: + group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }} + cancel-in-progress: true +defaults: + run: + shell: bash -e -l {0} +jobs: + build: + runs-on: ubuntu-24.04 + name: ${{ matrix.sys.compiler }} ${{ matrix.sys.version }} - ${{ matrix.sys.name }} + strategy: + fail-fast: false + matrix: + sys: + - {compiler: clang, version: '20', name: xsimd, flags: -DXTENSOR_USE_XSIMD=ON} + - {compiler: clang, version: '20', name: xsimd-tbb, flags: -DXTENSOR_USE_XSIMD=ON -DXTENSOR_USE_TBB=ON} + - {compiler: gcc, version: '14', name: xsimd, flags: -DXTENSOR_USE_XSIMD=ON} + - {compiler: gcc, version: '14', name: xsimd-tbb, flags: -DXTENSOR_USE_XSIMD=ON -DXTENSOR_USE_TBB=ON} + + steps: + - name: Install GCC + if: matrix.sys.compiler == 'gcc' + uses: egor-tensin/setup-gcc@v1 + with: + version: ${{matrix.sys.version}} + platform: x64 + + - name: Install LLVM and Clang + if: matrix.sys.compiler == 'clang' + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{matrix.sys.version}} + sudo apt-get install -y clang-tools-${{matrix.sys.version}} + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${{matrix.sys.version}} 200 + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{matrix.sys.version}} 200 + sudo update-alternatives --install /usr/bin/clang-scan-deps clang-scan-deps /usr/bin/clang-scan-deps-${{matrix.sys.version}} 200 + sudo update-alternatives --set clang /usr/bin/clang-${{matrix.sys.version}} + sudo update-alternatives --set clang++ /usr/bin/clang++-${{matrix.sys.version}} + sudo update-alternatives --set clang-scan-deps /usr/bin/clang-scan-deps-${{matrix.sys.version}} + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set conda environment + uses: mamba-org/setup-micromamba@main + with: + environment-name: myenv + environment-file: environment-dev.yml + init-shell: bash + cache-downloads: true + create-args: | + ${{ (matrix.sys.name == 'tbb' || matrix.sys.name == 'xsimd-tbb' ) && 'tbb-devel' || '' }} + + - name: Configure using CMake + run: | + if [[ "${{matrix.sys.compiler}}" = "gcc" ]]; then export CC=gcc-${{matrix.sys.version}}; export CXX=g++-${{matrix.sys.version}}; else export CC=clang; export CXX=clang++; fi + cmake -G Ninja -Bbuild -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DBUILD_BENCHMARK=ON ${{ matrix.sys.flags }} + + - name: Build + working-directory: build + run: cmake --build . --target benchmark_xtensor --parallel 8 + + - name: Run benchmark + timeout-minutes: 10 # Consider increasing timeout + working-directory: build/benchmark + run: ./benchmark_xtensor diff --git a/CMakeLists.txt b/CMakeLists.txt index f2a9fdf12..7c03dbb2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION 3.22) project(xtensor CXX) set(XTENSOR_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index c5fa3c407..9928eb155 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -6,7 +6,8 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.22) +include(FetchContent) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-benchmark) @@ -30,11 +31,11 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -g -Wunused-parameter -Wextra -Wreorder") if(NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") - CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG) - if (HAS_CPP14_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + CHECK_CXX_COMPILER_FLAG("-std=c++20" HAS_CPP20_FLAG) + if (HAS_CPP20_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") else() - message(FATAL_ERROR "Unsupported compiler -- xtensor requires C++14 support!") + message(FATAL_ERROR "Unsupported compiler -- xtensor requires C++17 support!") endif() endif() @@ -74,31 +75,17 @@ endif() if(DOWNLOAD_GBENCHMARK OR GBENCHMARK_SRC_DIR) - if(DOWNLOAD_GBENCHMARK) - # Download and unpack googlebenchmark at configure time - configure_file(downloadGBenchmark.cmake.in googlebenchmark-download/CMakeLists.txt) - else() - # Copy local source of googlebenchmark at configure time - configure_file(copyGBenchmark.cmake.in googlebenchmark-download/CMakeLists.txt) - endif() - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download ) - if(result) - message(FATAL_ERROR "CMake step for googlebenchmark failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download ) - if(result) - message(FATAL_ERROR "Build step for googlebenchmark failed: ${result}") - endif() + FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG main) - # Add googlebenchmark directly to our build. This defines - # the gtest and gtest_main targets. - add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src - ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build) + FetchContent_Declare(googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG main) # need main for benchmark::benchmark + FetchContent_MakeAvailable( + googletest + googlebenchmark) set(GBENCHMARK_INCLUDE_DIRS "${googlebenchmark_SOURCE_DIR}/include") set(GBENCHMARK_LIBRARIES benchmark) else() @@ -129,9 +116,11 @@ set(XTENSOR_BENCHMARK benchmark_view_access.cpp benchmark_view_assignment.cpp benchmark_view_adapt.cpp + benchmark_stl.cpp main.cpp ) + set(XTENSOR_BENCHMARK_TARGET benchmark_xtensor) add_executable(${XTENSOR_BENCHMARK_TARGET} EXCLUDE_FROM_ALL ${XTENSOR_BENCHMARK} ${XTENSOR_HEADERS}) target_link_libraries(${XTENSOR_BENCHMARK_TARGET} PUBLIC xtensor ${GBENCHMARK_LIBRARIES}) diff --git a/benchmark/benchmark_stl.cpp b/benchmark/benchmark_stl.cpp new file mode 100644 index 000000000..9ae160712 --- /dev/null +++ b/benchmark/benchmark_stl.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ****************************************************************************/ + +#include + +#include "xtensor/containers/xtensor.hpp" +#include "xtensor/core/xmath.hpp" +#include "xtensor/generators/xrandom.hpp" + +namespace xt +{ + namespace + { + constexpr std::array cContainerAssignShape{2000, 2000}; + + template + auto generateRandomInt16From0To100(Shape&& x) + { + return xt::random::randint(x, 0, 100); + } + } + + static void Xtensor_Uint16_2000x2000_DivideBy2_StdTransform(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + std::transform( + vInput.begin(), + vInput.end(), + vOutput.begin(), + [](auto&& aInputValue) + { + return aInputValue / 2; + } + ); + } + } + + static void Xtensor_Uint16_2000x2000_DivideBy2_Xtensor(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + vOutput = vInput / 2; + } + } + + static void Xtensor_Uint16_2000x2000_DivideBy2Double_StdTransform(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + std::transform( + vInput.begin(), + vInput.end(), + vOutput.begin(), + [](auto&& aInputValue) + { + return aInputValue / 2.0; + } + ); + } + } + + static void Xtensor_Uint16_2000x2000_DivideBy2Double_Xtensor(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + vOutput = vInput / 2.0; + } + } + + static void Xtensor_Uint16_2000x2000_MultiplyBy2_StdTransform(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + std::transform( + vInput.begin(), + vInput.end(), + vOutput.begin(), + [](auto&& aInputValue) + { + return aInputValue * 2; + } + ); + } + } + + static void Xtensor_Uint16_2000x2000_MultiplyBy2_Xtensor(benchmark::State& aState) + { + xt::xtensor vInput = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + vOutput = vInput * 2; + } + } + + static void Xtensor_Uint16_2000x2000_Maximum_StdTransform(benchmark::State& aState) + { + xt::xtensor vInput1 = generateRandomInt16From0To100(cContainerAssignShape); + xt::xtensor vInput2 = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + auto vInput2It = vInput2.begin(); + std::transform( + vInput1.begin(), + vInput1.end(), + vOutput.begin(), + [&vInput2It](auto&& aInput1Value) + { + return std::max(aInput1Value, *vInput2It++); + } + ); + } + } + + static void Xtensor_Uint16_2000x2000_Maximum_Xtensor(benchmark::State& aState) + { + xt::xtensor vInput1 = generateRandomInt16From0To100(cContainerAssignShape); + xt::xtensor vInput2 = generateRandomInt16From0To100(cContainerAssignShape); + auto vOutput = xt::xtensor::from_shape(cContainerAssignShape); + + for (auto _ : aState) + { + vOutput = xt::maximum(vInput1, vInput2); + } + } + + BENCHMARK(Xtensor_Uint16_2000x2000_Maximum_Xtensor); + BENCHMARK(Xtensor_Uint16_2000x2000_Maximum_StdTransform); + BENCHMARK(Xtensor_Uint16_2000x2000_MultiplyBy2_Xtensor); + BENCHMARK(Xtensor_Uint16_2000x2000_MultiplyBy2_StdTransform); + BENCHMARK(Xtensor_Uint16_2000x2000_DivideBy2Double_Xtensor); + BENCHMARK(Xtensor_Uint16_2000x2000_DivideBy2Double_StdTransform); +} diff --git a/benchmark/benchmark_view_assignment.cpp b/benchmark/benchmark_view_assignment.cpp index 4914d161f..3d5ae314b 100644 --- a/benchmark/benchmark_view_assignment.cpp +++ b/benchmark/benchmark_view_assignment.cpp @@ -155,6 +155,6 @@ namespace xt // BENCHMARK(assign_create_strided_view); BENCHMARK(assign_create_view); BENCHMARK(assign_create_manual_view); - BENCHMARK(data_offset); + // BENCHMARK(data_offset); BENCHMARK(data_offset_view); } diff --git a/benchmark/copyGBenchmark.cmake.in b/benchmark/copyGBenchmark.cmake.in index 753108438..e11eaed96 100644 --- a/benchmark/copyGBenchmark.cmake.in +++ b/benchmark/copyGBenchmark.cmake.in @@ -6,7 +6,7 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.5) project(googlebenchmark-download NONE) diff --git a/benchmark/downloadGBenchmark.cmake.in b/benchmark/downloadGBenchmark.cmake.in index eb8b2d029..7af348b1e 100644 --- a/benchmark/downloadGBenchmark.cmake.in +++ b/benchmark/downloadGBenchmark.cmake.in @@ -6,18 +6,19 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.5) project(googlebenchmark-download NONE) include(ExternalProject) ExternalProject_Add(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG v1.3.0 + GIT_TAG v1.9.4 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build" CONFIGURE_COMMAND "" BUILD_COMMAND "" + CMAKE_ARGS "BENCHMARK_DOWNLOAD_DEPENDENCIES=TRUE" INSTALL_COMMAND "" TEST_COMMAND "" )