| // Benchmarks for Eigen Tensor striding (subsampling 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; |
| |
| // --- Rank-2 read, outer dimension strided (inner runs stay contiguous) --- |
| static void BM_StrideReadOuter2D(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}; // ColMajor: dimension 0 is inner. |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = A.stride(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) { |
| if (B(i, j) != A(i, j * S)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Rank-2 read, inner dimension strided (gather within inner runs) --- |
| static void BM_StrideReadInner2D(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.stride(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) { |
| if (B(i, j) != A(i * S, j)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Identity striding (all strides 1, measures overhead) --- |
| static void BM_StrideIdentity2D(benchmark::State& state) { |
| const Index M = state.range(0); |
| const Index N = state.range(1); |
| |
| Tensor<Scalar, 2> A(M, N); |
| A.setRandom(); |
| Eigen::array<Index, 2> strides = {1, 1}; |
| |
| Tensor<Scalar, 2> B; |
| for (auto _ : state) { |
| B = A.stride(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) { |
| if (B(i, j) != A(i, j)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Rank-3 read, all dimensions strided --- |
| static void BM_StrideRead3D(benchmark::State& state) { |
| const Index D0 = state.range(0); |
| const Index D1 = state.range(1); |
| const Index D2 = state.range(2); |
| |
| Tensor<Scalar, 3> A(D0, D1, D2); |
| A.setRandom(); |
| Eigen::array<Index, 3> strides = {2, 2, 2}; |
| |
| Tensor<Scalar, 3> B; |
| for (auto _ : state) { |
| B = A.stride(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) { |
| for (Index k = 0; k < B.dimension(2); ++k) { |
| if (B(i, j, k) != A(i * 2, j * 2, k * 2)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Expression-sourced read (no raw buffer behind the striding) --- |
| static void BM_StrideExprInner2D(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 + A.constant(Scalar(1))).stride(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) { |
| if (B(i, j) != A(i * S, j) + Scalar(1)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * B.size()); |
| } |
| |
| // --- Rank-2 write through a strided destination --- |
| static void BM_StrideWriteOuter2D(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 + S - 1) / S); |
| A.setRandom(); |
| Tensor<Scalar, 2> C(M, N); |
| C.setZero(); |
| Eigen::array<Index, 2> strides = {1, S}; |
| |
| for (auto _ : state) { |
| C.stride(strides) = A; |
| benchmark::DoNotOptimize(C.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < A.dimension(0); ++i) { |
| for (Index j = 0; j < A.dimension(1); ++j) { |
| if (C(i, j * S) != A(i, j)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * A.size()); |
| } |
| |
| static void BM_StrideWriteInner2D(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 + S - 1) / S, N); |
| A.setRandom(); |
| Tensor<Scalar, 2> C(M, N); |
| C.setZero(); |
| Eigen::array<Index, 2> strides = {S, 1}; |
| |
| for (auto _ : state) { |
| C.stride(strides) = A; |
| benchmark::DoNotOptimize(C.data()); |
| benchmark::ClobberMemory(); |
| } |
| for (Index i = 0; i < A.dimension(0); ++i) { |
| for (Index j = 0; j < A.dimension(1); ++j) { |
| if (C(i * S, j) != A(i, j)) { |
| state.SkipWithError("validation failed"); |
| return; |
| } |
| } |
| } |
| state.SetItemsProcessed(state.iterations() * A.size()); |
| } |
| |
| // clang-format off |
| #define STRIDE_2D_SIZES ->ArgsProduct({{256, 1024}, {1024}, {2, 4}}) |
| #define STRIDE_2D_PLAIN_SIZES ->Args({256, 1024})->Args({1024, 1024}) |
| #define STRIDE_3D_SIZES ->Args({64, 64, 64})->Args({128, 128, 64}) |
| // clang-format on |
| |
| BENCHMARK(BM_StrideReadOuter2D) STRIDE_2D_SIZES; |
| BENCHMARK(BM_StrideReadInner2D) STRIDE_2D_SIZES; |
| BENCHMARK(BM_StrideIdentity2D) STRIDE_2D_PLAIN_SIZES; |
| BENCHMARK(BM_StrideRead3D) STRIDE_3D_SIZES; |
| BENCHMARK(BM_StrideExprInner2D) STRIDE_2D_SIZES; |
| BENCHMARK(BM_StrideWriteOuter2D) STRIDE_2D_SIZES; |
| BENCHMARK(BM_StrideWriteInner2D) STRIDE_2D_SIZES; |