| // Benchmarks for Eigen TensorInflation (zero-interleaved upsampling views). |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #include <benchmark/benchmark.h> |
| #include <contrib/Eigen/Tensor> |
| |
| using namespace Eigen; |
| |
| typedef float Scalar; |
| |
| // --- Inflate only the inner-most (contiguous) dimension. --- |
| static void BM_Inflate2D_Inner(benchmark::State& state) { |
| const Index M = state.range(0); |
| const Index N = state.range(1); |
| const Index S = state.range(2); |
| |
| Tensor<Scalar, 2> A(M, N); |
| A.setRandom(); |
| Eigen::array<Index, 2> strides = {S, 1}; |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = A.inflate(strides); |
| benchmark::DoNotOptimize(B.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < B.dimension(0); ++i) { |
| for (Index j = 0; j < B.dimension(1); ++j) { |
| const Scalar expected = (i % S == 0) ? A(i / S, j) : Scalar(0); |
| if (B(i, j) != expected) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Inflate only an outer dimension. Inner dim stays contiguous. --- |
| static void BM_Inflate2D_Outer(benchmark::State& state) { |
| const Index M = state.range(0); |
| const Index N = state.range(1); |
| const Index S = state.range(2); |
| |
| Tensor<Scalar, 2> A(M, N); |
| A.setRandom(); |
| Eigen::array<Index, 2> strides = {1, S}; |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = A.inflate(strides); |
| benchmark::DoNotOptimize(B.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < B.dimension(0); ++i) { |
| for (Index j = 0; j < B.dimension(1); ++j) { |
| const Scalar expected = (j % S == 0) ? A(i, j / S) : Scalar(0); |
| if (B(i, j) != expected) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Inflate every dimension. --- |
| static void BM_Inflate2D_All(benchmark::State& state) { |
| const Index M = state.range(0); |
| const Index N = state.range(1); |
| const Index S = state.range(2); |
| |
| Tensor<Scalar, 2> A(M, N); |
| A.setRandom(); |
| Eigen::array<Index, 2> strides = {S, S}; |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = A.inflate(strides); |
| benchmark::DoNotOptimize(B.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < B.dimension(0); ++i) { |
| for (Index j = 0; j < B.dimension(1); ++j) { |
| const Scalar expected = (i % S == 0 && j % S == 0) ? A(i / S, j / S) : Scalar(0); |
| if (B(i, j) != expected) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Expression-sourced inflation (no raw buffer behind the argument) --- |
| static void BM_Inflate2D_Expr(benchmark::State& state) { |
| const Index M = state.range(0); |
| const Index N = state.range(1); |
| const Index S = state.range(2); |
| |
| Tensor<Scalar, 2> A(M, N); |
| A.setRandom(); |
| Eigen::array<Index, 2> strides = {S, S}; |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = (A + A.constant(Scalar(1))).inflate(strides); |
| benchmark::DoNotOptimize(B.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < B.dimension(0); ++i) { |
| for (Index j = 0; j < B.dimension(1); ++j) { |
| const Scalar expected = (i % S == 0 && j % S == 0) ? A(i / S, j / S) + Scalar(1) : Scalar(0); |
| if (B(i, j) != expected) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // clang-format off |
| #define INFLATE_SIZES ->ArgsProduct({{256, 1024}, {1024}, {2, 4}}) |
| // clang-format on |
| |
| BENCHMARK(BM_Inflate2D_Inner) INFLATE_SIZES; |
| BENCHMARK(BM_Inflate2D_Outer) INFLATE_SIZES; |
| BENCHMARK(BM_Inflate2D_All) INFLATE_SIZES; |
| BENCHMARK(BM_Inflate2D_Expr) INFLATE_SIZES; |