| // Benchmarks for Eigen Tensor reductions (sum, maximum, mean). | |
| // Tests full and partial reductions, inner vs outer dimension, DefaultDevice and ThreadPoolDevice. | |
| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #define EIGEN_USE_THREADS | |
| #include <benchmark/benchmark.h> | |
| #include <contrib/Eigen/Tensor> | |
| #include <Eigen/ThreadPool> | |
| using namespace Eigen; | |
| #ifndef SCALAR | |
| #define SCALAR float | |
| #endif | |
| typedef SCALAR Scalar; | |
| // --- Full reduction (rank-2) --- | |
| template <typename ReduceOp> | |
| static void BM_FullReduction(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| Tensor<Scalar, 2> A(M, N); | |
| A.setRandom(); | |
| for (auto _ : state) { | |
| Tensor<Scalar, 0> result = A.reduce(Eigen::array<int, 2>{0, 1}, ReduceOp()); | |
| benchmark::DoNotOptimize(result.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar)); | |
| } | |
| // --- Partial reduction along dim 0 (inner dim, ColMajor) --- | |
| template <typename T> | |
| static void BM_ReduceInner_Typed(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| Tensor<T, 2> A(M, N); | |
| A.setRandom(); | |
| Eigen::array<int, 1> reduce_dims = {0}; | |
| for (auto _ : state) { | |
| Tensor<T, 1> result = A.sum(reduce_dims); | |
| benchmark::DoNotOptimize(result.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * M * N * sizeof(T)); | |
| } | |
| static void BM_ReduceInner(benchmark::State& state) { BM_ReduceInner_Typed<Scalar>(state); } | |
| // --- Partial reduction along dim 1 (outer dim, ColMajor) --- | |
| template <typename T> | |
| static void BM_ReduceOuter_Typed(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| Tensor<T, 2> A(M, N); | |
| A.setRandom(); | |
| Eigen::array<int, 1> reduce_dims = {1}; | |
| for (auto _ : state) { | |
| Tensor<T, 1> result = A.sum(reduce_dims); | |
| benchmark::DoNotOptimize(result.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * M * N * sizeof(T)); | |
| } | |
| static void BM_ReduceOuter(benchmark::State& state) { BM_ReduceOuter_Typed<Scalar>(state); } | |
| // --- Rank-4 partial reduction (batch x channels x H x W), reduce along spatial dims --- | |
| static void BM_ReduceSpatial(benchmark::State& state) { | |
| const int batch = state.range(0); | |
| const int C = state.range(1); | |
| const int H = state.range(2); | |
| Tensor<Scalar, 4> A(batch, C, H, H); | |
| A.setRandom(); | |
| Eigen::array<int, 2> reduce_dims = {2, 3}; | |
| for (auto _ : state) { | |
| Tensor<Scalar, 2> result = A.sum(reduce_dims); | |
| benchmark::DoNotOptimize(result.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * batch * C * H * H * sizeof(Scalar)); | |
| } | |
| // --- Full reduction with ThreadPoolDevice --- | |
| static void BM_FullReduction_ThreadPool(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int threads = state.range(2); | |
| Tensor<Scalar, 2> A(M, N); | |
| Tensor<Scalar, 0> result; | |
| A.setRandom(); | |
| ThreadPool tp(threads); | |
| ThreadPoolDevice dev(&tp, threads); | |
| for (auto _ : state) { | |
| result.device(dev) = A.sum(); | |
| benchmark::DoNotOptimize(result.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar)); | |
| state.counters["threads"] = threads; | |
| } | |
| // clang-format off | |
| #define REDUCTION_SIZES \ | |
| ->Args({64, 64})->Args({256, 256})->Args({1024, 1024}) | |
| #define THREADPOOL_REDUCTION_SIZES \ | |
| ->Args({256, 256, 2})->Args({256, 256, 4})->Args({256, 256, 8}) \ | |
| ->Args({1024, 1024, 2})->Args({1024, 1024, 4})->Args({1024, 1024, 8}) | |
| // {batch, channels, h}: pure Cartesian product. | |
| #define SPATIAL_SIZES ->ArgsProduct({{1, 8, 32}, {64, 128}, {16, 32}}) | |
| // clang-format on | |
| BENCHMARK(BM_FullReduction<internal::SumReducer<Scalar>>) REDUCTION_SIZES->Name("SumReduction"); | |
| BENCHMARK(BM_FullReduction<internal::MaxReducer<Scalar>>) REDUCTION_SIZES->Name("MaxReduction_Full"); | |
| BENCHMARK(BM_ReduceInner) REDUCTION_SIZES; | |
| BENCHMARK(BM_ReduceOuter) REDUCTION_SIZES; | |
| BENCHMARK_TEMPLATE(BM_ReduceInner_Typed, Eigen::half) REDUCTION_SIZES; | |
| BENCHMARK_TEMPLATE(BM_ReduceOuter_Typed, Eigen::half) REDUCTION_SIZES; | |
| BENCHMARK_TEMPLATE(BM_ReduceInner_Typed, Eigen::bfloat16) REDUCTION_SIZES; | |
| BENCHMARK_TEMPLATE(BM_ReduceOuter_Typed, Eigen::bfloat16) REDUCTION_SIZES; | |
| BENCHMARK(BM_ReduceSpatial) SPATIAL_SIZES; | |
| BENCHMARK(BM_FullReduction_ThreadPool) THREADPOOL_REDUCTION_SIZES; |