| // Benchmarks for Eigen TensorRoll. | |
| // 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; | |
| // --- Roll only the inner-most (contiguous) dimension. --- | |
| static void BM_Roll_Inner(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> A(M, N); | |
| A.setRandom(); | |
| array<Index, 2> rolls = {shift, 0}; | |
| for (auto _ : state) { | |
| Tensor<Scalar, 2> B = A.roll(rolls); | |
| benchmark::DoNotOptimize(B.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| // --- Roll only an outer dimension. Inner dim stays contiguous. --- | |
| static void BM_Roll_Outer(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> A(M, N); | |
| A.setRandom(); | |
| array<Index, 2> rolls = {0, shift}; | |
| for (auto _ : state) { | |
| Tensor<Scalar, 2> B = A.roll(rolls); | |
| benchmark::DoNotOptimize(B.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| // --- Roll every dimension. --- | |
| static void BM_Roll_All(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> A(M, N); | |
| A.setRandom(); | |
| array<Index, 2> rolls = {shift, shift}; | |
| for (auto _ : state) { | |
| Tensor<Scalar, 2> B = A.roll(rolls); | |
| benchmark::DoNotOptimize(B.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| // --- 3D roll with the inner dim shifted. --- | |
| static void BM_Roll_3D_Inner(benchmark::State& state) { | |
| const int D0 = state.range(0); | |
| const int D1 = state.range(1); | |
| const int D2 = state.range(2); | |
| Tensor<Scalar, 3> A(D0, D1, D2); | |
| A.setRandom(); | |
| array<Index, 3> rolls = {D0 / 4, 0, 0}; | |
| for (auto _ : state) { | |
| Tensor<Scalar, 3> B = A.roll(rolls); | |
| benchmark::DoNotOptimize(B.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(D0) * D1 * D2 * sizeof(Scalar)); | |
| } | |
| // --- Writes through a rolled destination (lvalue writeBlock path, which | |
| // scatters each block into its wrap-around pieces) --- | |
| static void BM_RollWrite_Inner(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> src(M, N); | |
| src.setRandom(); | |
| Tensor<Scalar, 2> dst(M, N); | |
| dst.setZero(); | |
| array<Index, 2> rolls = {shift, 0}; | |
| for (auto _ : state) { | |
| dst.roll(rolls) = src; | |
| benchmark::DoNotOptimize(dst.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| for (int i = 0; i < M; ++i) { | |
| for (int j = 0; j < N; ++j) { | |
| if (dst((i + shift) % M, j) != src(i, j)) { | |
| state.SkipWithError("validation failed"); | |
| return; | |
| } | |
| } | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| static void BM_RollWrite_Outer(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> src(M, N); | |
| src.setRandom(); | |
| Tensor<Scalar, 2> dst(M, N); | |
| dst.setZero(); | |
| array<Index, 2> rolls = {0, shift}; | |
| for (auto _ : state) { | |
| dst.roll(rolls) = src; | |
| benchmark::DoNotOptimize(dst.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| for (int i = 0; i < M; ++i) { | |
| for (int j = 0; j < N; ++j) { | |
| if (dst(i, (j + shift) % N) != src(i, j)) { | |
| state.SkipWithError("validation failed"); | |
| return; | |
| } | |
| } | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| // Lazy right-hand-side blocks: writeBlock materializes the cwise expression | |
| // into a temporary before scattering the pieces. | |
| static void BM_RollWrite_Expr(benchmark::State& state) { | |
| const int M = state.range(0); | |
| const int N = state.range(1); | |
| const int shift = state.range(2); | |
| Tensor<Scalar, 2> src(M, N); | |
| src.setRandom(); | |
| Tensor<Scalar, 2> dst(M, N); | |
| dst.setZero(); | |
| array<Index, 2> rolls = {shift, shift}; | |
| for (auto _ : state) { | |
| dst.roll(rolls) = src + src.constant(1.0f); | |
| benchmark::DoNotOptimize(dst.data()); | |
| benchmark::ClobberMemory(); | |
| } | |
| for (int i = 0; i < M; ++i) { | |
| for (int j = 0; j < N; ++j) { | |
| if (dst((i + shift) % M, (j + shift) % N) != src(i, j) + 1.0f) { | |
| state.SkipWithError("validation failed"); | |
| return; | |
| } | |
| } | |
| } | |
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(M) * N * sizeof(Scalar)); | |
| } | |
| // clang-format off | |
| #define ROLL_SIZES \ | |
| ->Args({64, 64, 1})->Args({64, 64, 13}) \ | |
| ->Args({256, 256, 1})->Args({256, 256, 13}) \ | |
| ->Args({1024, 1024, 1})->Args({1024, 1024, 13}) | |
| // Transition sizes around the tiled-evaluation crossover (~8x8) for the | |
| // write benchmarks: below it the fixed tiled-executor setup cost dominates. | |
| #define ROLL_WRITE_SIZES \ | |
| ->Args({2, 2, 1})->Args({4, 4, 1})->Args({8, 8, 1})->Args({16, 16, 3})->Args({32, 32, 3}) \ | |
| ->Args({64, 64, 1})->Args({64, 64, 13}) \ | |
| ->Args({256, 256, 1})->Args({256, 256, 13}) \ | |
| ->Args({1024, 1024, 1})->Args({1024, 1024, 13}) | |
| #define ROLL_3D_SIZES \ | |
| ->Args({32, 32, 32})->Args({64, 64, 64})->Args({128, 128, 128}) | |
| // clang-format on | |
| BENCHMARK(BM_Roll_Inner) ROLL_SIZES; | |
| BENCHMARK(BM_Roll_Outer) ROLL_SIZES; | |
| BENCHMARK(BM_Roll_All) ROLL_SIZES; | |
| BENCHMARK(BM_Roll_3D_Inner) ROLL_3D_SIZES; | |
| BENCHMARK(BM_RollWrite_Inner) ROLL_WRITE_SIZES; | |
| BENCHMARK(BM_RollWrite_Outer) ROLL_WRITE_SIZES; | |
| BENCHMARK(BM_RollWrite_Expr) ROLL_WRITE_SIZES; |