mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
Add benchmarks for unsupported modules and extend supported benchmarks
libeigen/eigen!2179 Closes #3036 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
This commit is contained in:
8
unsupported/benchmarks/Tensor/CMakeLists.txt
Normal file
8
unsupported/benchmarks/Tensor/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
eigen_add_benchmark(bench_contraction bench_contraction.cpp)
|
||||
eigen_add_benchmark(bench_convolution bench_convolution.cpp)
|
||||
eigen_add_benchmark(bench_reduction bench_reduction.cpp)
|
||||
eigen_add_benchmark(bench_broadcasting bench_broadcasting.cpp)
|
||||
eigen_add_benchmark(bench_shuffling bench_shuffling.cpp)
|
||||
eigen_add_benchmark(bench_tensor_fft bench_tensor_fft.cpp)
|
||||
eigen_add_benchmark(bench_morphing bench_morphing.cpp)
|
||||
eigen_add_benchmark(bench_coefficient_wise bench_coefficient_wise.cpp)
|
||||
111
unsupported/benchmarks/Tensor/bench_broadcasting.cpp
Normal file
111
unsupported/benchmarks/Tensor/bench_broadcasting.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// Benchmarks for Eigen Tensor broadcasting.
|
||||
// Tests broadcasting along various dimensions and ranks.
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
typedef float Scalar;
|
||||
|
||||
// --- Broadcast row vector {1,N} -> {M,N} ---
|
||||
static void BM_BroadcastRow(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> row(1, N);
|
||||
Tensor<Scalar, 2> result(M, N);
|
||||
row.setRandom();
|
||||
|
||||
Eigen::array<int, 2> bcast = {M, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
result = row.broadcast(bcast);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Broadcast col vector {M,1} -> {M,N} ---
|
||||
static void BM_BroadcastCol(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> col(M, 1);
|
||||
Tensor<Scalar, 2> result(M, N);
|
||||
col.setRandom();
|
||||
|
||||
Eigen::array<int, 2> bcast = {1, N};
|
||||
|
||||
for (auto _ : state) {
|
||||
result = col.broadcast(bcast);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Broadcast + element-wise add (bias addition pattern) ---
|
||||
static void BM_BroadcastAdd(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> mat(M, N);
|
||||
Tensor<Scalar, 2> bias(1, N);
|
||||
Tensor<Scalar, 2> result(M, N);
|
||||
mat.setRandom();
|
||||
bias.setRandom();
|
||||
|
||||
Eigen::array<int, 2> bcast = {M, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
result = mat + bias.broadcast(bcast);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
// --- Rank-4 broadcast (batch x channels x 1 x 1) -> (batch x channels x H x W) ---
|
||||
static void BM_BroadcastRank4(benchmark::State& state) {
|
||||
const int batch = state.range(0);
|
||||
const int C = state.range(1);
|
||||
const int H = state.range(2);
|
||||
|
||||
Tensor<Scalar, 4> bias(batch, C, 1, 1);
|
||||
Tensor<Scalar, 4> result(batch, C, H, H);
|
||||
bias.setRandom();
|
||||
|
||||
Eigen::array<int, 4> bcast = {1, 1, H, H};
|
||||
|
||||
for (auto _ : state) {
|
||||
result = bias.broadcast(bcast);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * batch * C * H * H * sizeof(Scalar));
|
||||
}
|
||||
|
||||
static void BroadcastSizes(::benchmark::Benchmark* b) {
|
||||
for (int m : {64, 256, 1024}) {
|
||||
for (int n : {64, 256, 1024}) {
|
||||
b->Args({m, n});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Rank4Sizes(::benchmark::Benchmark* b) {
|
||||
for (int batch : {1, 8}) {
|
||||
for (int c : {64, 256}) {
|
||||
for (int h : {16, 32}) {
|
||||
b->Args({batch, c, h});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_BroadcastRow)->Apply(BroadcastSizes);
|
||||
BENCHMARK(BM_BroadcastCol)->Apply(BroadcastSizes);
|
||||
BENCHMARK(BM_BroadcastAdd)->Apply(BroadcastSizes);
|
||||
BENCHMARK(BM_BroadcastRank4)->Apply(Rank4Sizes);
|
||||
131
unsupported/benchmarks/Tensor/bench_coefficient_wise.cpp
Normal file
131
unsupported/benchmarks/Tensor/bench_coefficient_wise.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// Benchmarks for Eigen Tensor coefficient-wise operations.
|
||||
// Covers activation functions, normalization, and element-wise arithmetic.
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
typedef float Scalar;
|
||||
|
||||
// Macro to define a benchmark for a unary tensor operation.
|
||||
#define BENCH_TENSOR_UNARY(NAME, EXPR) \
|
||||
static void BM_##NAME(benchmark::State& state) { \
|
||||
const int M = state.range(0); \
|
||||
const int N = state.range(1); \
|
||||
Tensor<Scalar, 2> a(M, N); \
|
||||
a.setRandom(); \
|
||||
Tensor<Scalar, 2> b(M, N); \
|
||||
for (auto _ : state) { \
|
||||
b = EXPR; \
|
||||
benchmark::DoNotOptimize(b.data()); \
|
||||
benchmark::ClobberMemory(); \
|
||||
} \
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 2); \
|
||||
}
|
||||
|
||||
BENCH_TENSOR_UNARY(Exp, a.exp())
|
||||
BENCH_TENSOR_UNARY(Log, a.abs().log())
|
||||
BENCH_TENSOR_UNARY(Tanh, a.tanh())
|
||||
BENCH_TENSOR_UNARY(Sigmoid, a.sigmoid())
|
||||
BENCH_TENSOR_UNARY(ReLU, a.cwiseMax(Scalar(0)))
|
||||
BENCH_TENSOR_UNARY(Sqrt, a.abs().sqrt())
|
||||
|
||||
// --- Element-wise binary operations ---
|
||||
static void BM_Add(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> a(M, N);
|
||||
Tensor<Scalar, 2> b(M, N);
|
||||
Tensor<Scalar, 2> c(M, N);
|
||||
a.setRandom();
|
||||
b.setRandom();
|
||||
|
||||
for (auto _ : state) {
|
||||
c = a + b;
|
||||
benchmark::DoNotOptimize(c.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 3);
|
||||
}
|
||||
|
||||
static void BM_Mul(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> a(M, N);
|
||||
Tensor<Scalar, 2> b(M, N);
|
||||
Tensor<Scalar, 2> c(M, N);
|
||||
a.setRandom();
|
||||
b.setRandom();
|
||||
|
||||
for (auto _ : state) {
|
||||
c = a * b;
|
||||
benchmark::DoNotOptimize(c.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 3);
|
||||
}
|
||||
|
||||
// --- Fused multiply-add ---
|
||||
static void BM_FMA(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> a(M, N);
|
||||
Tensor<Scalar, 2> b(M, N);
|
||||
Tensor<Scalar, 2> c(M, N);
|
||||
Tensor<Scalar, 2> d(M, N);
|
||||
a.setRandom();
|
||||
b.setRandom();
|
||||
c.setRandom();
|
||||
|
||||
for (auto _ : state) {
|
||||
d = a * b + c;
|
||||
benchmark::DoNotOptimize(d.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 4);
|
||||
}
|
||||
|
||||
// --- Rank-4 coefficient-wise (CNN feature maps) ---
|
||||
static void BM_ReLU_Rank4(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);
|
||||
Tensor<Scalar, 4> b(batch, C, H, H);
|
||||
a.setRandom();
|
||||
|
||||
for (auto _ : state) {
|
||||
b = a.cwiseMax(Scalar(0));
|
||||
benchmark::DoNotOptimize(b.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * batch * C * H * H * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
static void CwiseSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
b->Args({size, size});
|
||||
}
|
||||
}
|
||||
|
||||
static void Rank4Sizes(::benchmark::Benchmark* b) {
|
||||
b->Args({32, 64, 16});
|
||||
b->Args({8, 128, 32});
|
||||
b->Args({1, 256, 64});
|
||||
}
|
||||
|
||||
BENCHMARK(BM_Exp)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Log)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Tanh)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Sigmoid)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_ReLU)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Sqrt)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Add)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_Mul)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_FMA)->Apply(CwiseSizes);
|
||||
BENCHMARK(BM_ReLU_Rank4)->Apply(Rank4Sizes);
|
||||
148
unsupported/benchmarks/Tensor/bench_contraction.cpp
Normal file
148
unsupported/benchmarks/Tensor/bench_contraction.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// Benchmarks for Eigen Tensor contraction (generalized GEMM).
|
||||
// Tests single-threaded (DefaultDevice) and multi-threaded (ThreadPoolDevice) variants.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
#include <unsupported/Eigen/CXX11/ThreadPool>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
#ifndef SCALAR
|
||||
#define SCALAR float
|
||||
#endif
|
||||
|
||||
typedef SCALAR Scalar;
|
||||
|
||||
// --- DefaultDevice contraction (rank-2, equivalent to matrix multiply) ---
|
||||
static void BM_Contraction(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
const int K = state.range(2);
|
||||
|
||||
Tensor<Scalar, 2> A(M, K);
|
||||
Tensor<Scalar, 2> B(K, N);
|
||||
Tensor<Scalar, 2> C(M, N);
|
||||
A.setRandom();
|
||||
B.setRandom();
|
||||
|
||||
using ContractDims = Tensor<Scalar, 2>::DimensionPair;
|
||||
Eigen::array<ContractDims, 1> contract_dims = {ContractDims(1, 0)};
|
||||
|
||||
for (auto _ : state) {
|
||||
C = A.contract(B, contract_dims);
|
||||
benchmark::DoNotOptimize(C.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(2.0 * M * N * K, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- ThreadPoolDevice contraction ---
|
||||
static void BM_Contraction_ThreadPool(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
const int K = state.range(2);
|
||||
const int threads = state.range(3);
|
||||
|
||||
Tensor<Scalar, 2> A(M, K);
|
||||
Tensor<Scalar, 2> B(K, N);
|
||||
Tensor<Scalar, 2> C(M, N);
|
||||
A.setRandom();
|
||||
B.setRandom();
|
||||
|
||||
ThreadPool tp(threads);
|
||||
ThreadPoolDevice dev(&tp, threads);
|
||||
|
||||
using ContractDims = Tensor<Scalar, 2>::DimensionPair;
|
||||
Eigen::array<ContractDims, 1> contract_dims = {ContractDims(1, 0)};
|
||||
|
||||
for (auto _ : state) {
|
||||
C.device(dev) = A.contract(B, contract_dims);
|
||||
benchmark::DoNotOptimize(C.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(2.0 * M * N * K, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
state.counters["threads"] = threads;
|
||||
}
|
||||
|
||||
// --- Rank-3 batch contraction ---
|
||||
static void BM_BatchContraction(benchmark::State& state) {
|
||||
const int batch = state.range(0);
|
||||
const int M = state.range(1);
|
||||
const int N = state.range(2);
|
||||
const int K = state.range(3);
|
||||
|
||||
Tensor<Scalar, 3> A(batch, M, K);
|
||||
Tensor<Scalar, 3> B(batch, K, N);
|
||||
Tensor<Scalar, 3> C(batch, M, N);
|
||||
A.setRandom();
|
||||
B.setRandom();
|
||||
|
||||
using ContractDims = Tensor<Scalar, 3>::DimensionPair;
|
||||
Eigen::array<ContractDims, 1> contract_dims = {ContractDims(2, 1)};
|
||||
|
||||
for (auto _ : state) {
|
||||
C = A.contract(B, contract_dims);
|
||||
benchmark::DoNotOptimize(C.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.counters["GFLOPS"] = benchmark::Counter(2.0 * batch * M * N * K, benchmark::Counter::kIsIterationInvariantRate,
|
||||
benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- RowMajor contraction ---
|
||||
static void BM_Contraction_RowMajor(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
const int K = state.range(2);
|
||||
|
||||
Tensor<Scalar, 2, RowMajor> A(M, K);
|
||||
Tensor<Scalar, 2, RowMajor> B(K, N);
|
||||
Tensor<Scalar, 2, RowMajor> C(M, N);
|
||||
A.setRandom();
|
||||
B.setRandom();
|
||||
|
||||
using ContractDims = Tensor<Scalar, 2, RowMajor>::DimensionPair;
|
||||
Eigen::array<ContractDims, 1> contract_dims = {ContractDims(1, 0)};
|
||||
|
||||
for (auto _ : state) {
|
||||
C = A.contract(B, contract_dims);
|
||||
benchmark::DoNotOptimize(C.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(2.0 * M * N * K, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
static void ContractionSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {32, 64, 128, 256, 512, 1024}) {
|
||||
b->Args({size, size, size});
|
||||
}
|
||||
// Non-square
|
||||
b->Args({256, 256, 1024});
|
||||
b->Args({1024, 64, 64});
|
||||
}
|
||||
|
||||
static void ThreadPoolSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {64, 256, 512, 1024}) {
|
||||
for (int threads : {2, 4, 8}) {
|
||||
b->Args({size, size, size, threads});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BatchSizes(::benchmark::Benchmark* b) {
|
||||
for (int batch : {1, 8, 32}) {
|
||||
for (int size : {64, 256}) {
|
||||
b->Args({batch, size, size, size});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_Contraction)->Apply(ContractionSizes);
|
||||
BENCHMARK(BM_Contraction_RowMajor)->Apply(ContractionSizes);
|
||||
BENCHMARK(BM_Contraction_ThreadPool)->Apply(ThreadPoolSizes);
|
||||
BENCHMARK(BM_BatchContraction)->Apply(BatchSizes);
|
||||
151
unsupported/benchmarks/Tensor/bench_convolution.cpp
Normal file
151
unsupported/benchmarks/Tensor/bench_convolution.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
// Benchmarks for Eigen Tensor convolution (1D and 2D).
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
#include <unsupported/Eigen/CXX11/ThreadPool>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
typedef float Scalar;
|
||||
|
||||
// --- 1D convolution ---
|
||||
static void BM_Convolve1D(benchmark::State& state) {
|
||||
const int input_size = state.range(0);
|
||||
const int kernel_size = state.range(1);
|
||||
|
||||
Tensor<Scalar, 1> input(input_size);
|
||||
Tensor<Scalar, 1> kernel(kernel_size);
|
||||
input.setRandom();
|
||||
kernel.setRandom();
|
||||
|
||||
Eigen::array<int, 1> dims = {0};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 1> result = input.convolve(kernel, dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
double flops = 2.0 * (input_size - kernel_size + 1) * kernel_size;
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(flops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- 2D convolution ---
|
||||
static void BM_Convolve2D(benchmark::State& state) {
|
||||
const int H = state.range(0);
|
||||
const int W = state.range(1);
|
||||
const int kH = state.range(2);
|
||||
const int kW = state.range(3);
|
||||
|
||||
Tensor<Scalar, 2> input(H, W);
|
||||
Tensor<Scalar, 2> kernel(kH, kW);
|
||||
input.setRandom();
|
||||
kernel.setRandom();
|
||||
|
||||
Eigen::array<int, 2> dims = {0, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 2> result = input.convolve(kernel, dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
double flops = 2.0 * (H - kH + 1) * (W - kW + 1) * kH * kW;
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(flops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- 2D convolution with channels (rank-3: C x H x W, convolve on H,W) ---
|
||||
static void BM_Convolve2D_Channels(benchmark::State& state) {
|
||||
const int C = state.range(0);
|
||||
const int H = state.range(1);
|
||||
const int kH = state.range(2);
|
||||
|
||||
Tensor<Scalar, 3> input(C, H, H);
|
||||
Tensor<Scalar, 2> kernel(kH, kH);
|
||||
input.setRandom();
|
||||
kernel.setRandom();
|
||||
|
||||
Eigen::array<int, 2> dims = {1, 2};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 3> result = input.convolve(kernel, dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
int outH = H - kH + 1;
|
||||
double flops = 2.0 * C * outH * outH * kH * kH;
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(flops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- 2D convolution with ThreadPool ---
|
||||
static void BM_Convolve2D_ThreadPool(benchmark::State& state) {
|
||||
const int H = state.range(0);
|
||||
const int kH = state.range(1);
|
||||
const int threads = state.range(2);
|
||||
|
||||
Tensor<Scalar, 2> input(H, H);
|
||||
Tensor<Scalar, 2> kernel(kH, kH);
|
||||
Tensor<Scalar, 2> result(H - kH + 1, H - kH + 1);
|
||||
input.setRandom();
|
||||
kernel.setRandom();
|
||||
|
||||
ThreadPool tp(threads);
|
||||
ThreadPoolDevice dev(&tp, threads);
|
||||
|
||||
Eigen::array<int, 2> dims = {0, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
result.device(dev) = input.convolve(kernel, dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
int outH = H - kH + 1;
|
||||
double flops = 2.0 * outH * outH * kH * kH;
|
||||
state.counters["GFLOPS"] =
|
||||
benchmark::Counter(flops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
state.counters["threads"] = threads;
|
||||
}
|
||||
|
||||
static void Conv1DSizes(::benchmark::Benchmark* b) {
|
||||
for (int input : {128, 512, 2048}) {
|
||||
for (int kernel : {3, 5, 11}) {
|
||||
b->Args({input, kernel});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Conv2DSizes(::benchmark::Benchmark* b) {
|
||||
for (int hw : {32, 64, 128, 224}) {
|
||||
for (int k : {3, 5, 7}) {
|
||||
b->Args({hw, hw, k, k});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Conv2DChannelSizes(::benchmark::Benchmark* b) {
|
||||
for (int c : {3, 64, 128}) {
|
||||
for (int hw : {16, 32, 56}) {
|
||||
for (int k : {3, 5}) {
|
||||
b->Args({c, hw, k});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Conv2DThreadPoolSizes(::benchmark::Benchmark* b) {
|
||||
for (int hw : {64, 128, 224}) {
|
||||
for (int k : {3, 5}) {
|
||||
for (int threads : {2, 4, 8}) {
|
||||
b->Args({hw, k, threads});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_Convolve1D)->Apply(Conv1DSizes);
|
||||
BENCHMARK(BM_Convolve2D)->Apply(Conv2DSizes);
|
||||
BENCHMARK(BM_Convolve2D_Channels)->Apply(Conv2DChannelSizes);
|
||||
BENCHMARK(BM_Convolve2D_ThreadPool)->Apply(Conv2DThreadPoolSizes);
|
||||
142
unsupported/benchmarks/Tensor/bench_morphing.cpp
Normal file
142
unsupported/benchmarks/Tensor/bench_morphing.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// Benchmarks for Eigen Tensor morphing operations: reshape, slice, chip, pad, stride.
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
typedef float Scalar;
|
||||
|
||||
// --- Reshape (zero-cost if no evaluation needed; force eval via assignment) ---
|
||||
static void BM_Reshape(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<Index, 1> new_shape = {M * N};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 1> B = A.reshape(new_shape);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Slice ---
|
||||
static void BM_Slice(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
int sliceM = M / 2;
|
||||
int sliceN = N / 2;
|
||||
Eigen::array<Index, 2> offsets = {0, 0};
|
||||
Eigen::array<Index, 2> extents = {sliceM, sliceN};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 2> B = A.slice(offsets, extents);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * sliceM * sliceN * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Chip (extract a sub-tensor along one dimension) ---
|
||||
static void BM_Chip(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();
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 2> B = A.chip(0, 0);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * D1 * D2 * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Pad ---
|
||||
static void BM_Pad(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
const int padSize = state.range(2);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<std::pair<int, int>, 2> paddings;
|
||||
paddings[0] = {padSize, padSize};
|
||||
paddings[1] = {padSize, padSize};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 2> B = A.pad(paddings);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
int outM = M + 2 * padSize;
|
||||
int outN = N + 2 * padSize;
|
||||
state.SetBytesProcessed(state.iterations() * outM * outN * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Stride ---
|
||||
static void BM_Stride(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
const int stride = state.range(2);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<Index, 2> strides_arr = {stride, stride};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 2> B = A.stride(strides_arr);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
int outM = (M + stride - 1) / stride;
|
||||
int outN = (N + stride - 1) / stride;
|
||||
state.SetBytesProcessed(state.iterations() * outM * outN * sizeof(Scalar));
|
||||
}
|
||||
|
||||
static void MorphSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
b->Args({size, size});
|
||||
}
|
||||
}
|
||||
|
||||
static void ChipSizes(::benchmark::Benchmark* b) {
|
||||
b->Args({32, 256, 256});
|
||||
b->Args({64, 128, 128});
|
||||
b->Args({8, 512, 512});
|
||||
}
|
||||
|
||||
static void PadSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
for (int pad : {1, 4, 16}) {
|
||||
b->Args({size, size, pad});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void StrideSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
for (int stride : {2, 4}) {
|
||||
b->Args({size, size, stride});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_Reshape)->Apply(MorphSizes);
|
||||
BENCHMARK(BM_Slice)->Apply(MorphSizes);
|
||||
BENCHMARK(BM_Chip)->Apply(ChipSizes);
|
||||
BENCHMARK(BM_Pad)->Apply(PadSizes);
|
||||
BENCHMARK(BM_Stride)->Apply(StrideSizes);
|
||||
158
unsupported/benchmarks/Tensor/bench_reduction.cpp
Normal file
158
unsupported/benchmarks/Tensor/bench_reduction.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
// Benchmarks for Eigen Tensor reductions (sum, maximum, mean).
|
||||
// Tests full and partial reductions, inner vs outer dimension, DefaultDevice and ThreadPoolDevice.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
#include <unsupported/Eigen/CXX11/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) ---
|
||||
static void BM_ReduceInner(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<int, 1> reduce_dims = {0};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 1> result = A.sum(reduce_dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- Partial reduction along dim 1 (outer dim, ColMajor) ---
|
||||
static void BM_ReduceOuter(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<int, 1> reduce_dims = {1};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 1> result = A.sum(reduce_dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
// --- 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;
|
||||
}
|
||||
|
||||
// --- Maximum reduction (rank-2) ---
|
||||
static void BM_MaxReduction(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.maximum();
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar));
|
||||
}
|
||||
|
||||
static void ReductionSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {64, 256, 1024}) {
|
||||
b->Args({size, size});
|
||||
}
|
||||
}
|
||||
|
||||
static void ThreadPoolReductionSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
for (int threads : {2, 4, 8}) {
|
||||
b->Args({size, size, threads});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SpatialSizes(::benchmark::Benchmark* b) {
|
||||
for (int batch : {1, 8, 32}) {
|
||||
for (int c : {64, 128}) {
|
||||
for (int h : {16, 32}) {
|
||||
b->Args({batch, c, h});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_FullReduction<internal::SumReducer<Scalar>>)->Apply(ReductionSizes)->Name("SumReduction");
|
||||
BENCHMARK(BM_FullReduction<internal::MaxReducer<Scalar>>)->Apply(ReductionSizes)->Name("MaxReduction_Full");
|
||||
BENCHMARK(BM_MaxReduction)->Apply(ReductionSizes);
|
||||
BENCHMARK(BM_ReduceInner)->Apply(ReductionSizes);
|
||||
BENCHMARK(BM_ReduceOuter)->Apply(ReductionSizes);
|
||||
BENCHMARK(BM_ReduceSpatial)->Apply(SpatialSizes);
|
||||
BENCHMARK(BM_FullReduction_ThreadPool)->Apply(ThreadPoolReductionSizes);
|
||||
115
unsupported/benchmarks/Tensor/bench_shuffling.cpp
Normal file
115
unsupported/benchmarks/Tensor/bench_shuffling.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Benchmarks for Eigen Tensor shuffling (transpose / permutation).
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
typedef float Scalar;
|
||||
|
||||
// --- Rank-2 transpose ---
|
||||
static void BM_Shuffle2D(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
Tensor<Scalar, 2> B(N, M);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<int, 2> perm = {1, 0};
|
||||
|
||||
for (auto _ : state) {
|
||||
B = A.shuffle(perm);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
// --- Identity shuffle (no permutation, measures overhead) ---
|
||||
static void BM_ShuffleIdentity(benchmark::State& state) {
|
||||
const int M = state.range(0);
|
||||
const int N = state.range(1);
|
||||
|
||||
Tensor<Scalar, 2> A(M, N);
|
||||
Tensor<Scalar, 2> B(M, N);
|
||||
A.setRandom();
|
||||
|
||||
Eigen::array<int, 2> perm = {0, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
B = A.shuffle(perm);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * M * N * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
// --- Rank-3 permutation ---
|
||||
static void BM_Shuffle3D(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();
|
||||
|
||||
// Permutation (2, 0, 1)
|
||||
Eigen::array<int, 3> perm = {2, 0, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 3> B = A.shuffle(perm);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * D0 * D1 * D2 * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
// --- Rank-4 permutation (NCHW -> NHWC layout conversion) ---
|
||||
static void BM_Shuffle4D_NCHW_to_NHWC(benchmark::State& state) {
|
||||
const int N = state.range(0);
|
||||
const int C = state.range(1);
|
||||
const int H = state.range(2);
|
||||
|
||||
Tensor<Scalar, 4> A(N, C, H, H);
|
||||
A.setRandom();
|
||||
|
||||
// NCHW -> NHWC: permute (0, 2, 3, 1)
|
||||
Eigen::array<int, 4> perm = {0, 2, 3, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<Scalar, 4> B = A.shuffle(perm);
|
||||
benchmark::DoNotOptimize(B.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
state.SetBytesProcessed(state.iterations() * N * C * H * H * sizeof(Scalar) * 2);
|
||||
}
|
||||
|
||||
static void Shuffle2DSizes(::benchmark::Benchmark* b) {
|
||||
for (int size : {256, 1024}) {
|
||||
b->Args({size, size});
|
||||
}
|
||||
b->Args({64, 4096});
|
||||
b->Args({4096, 64});
|
||||
}
|
||||
|
||||
static void Shuffle3DSizes(::benchmark::Benchmark* b) {
|
||||
b->Args({64, 64, 64});
|
||||
b->Args({128, 128, 64});
|
||||
b->Args({32, 256, 256});
|
||||
}
|
||||
|
||||
static void Shuffle4DSizes(::benchmark::Benchmark* b) {
|
||||
for (int batch : {1, 8}) {
|
||||
for (int c : {3, 64}) {
|
||||
for (int h : {32, 64}) {
|
||||
b->Args({batch, c, h});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_Shuffle2D)->Apply(Shuffle2DSizes);
|
||||
BENCHMARK(BM_ShuffleIdentity)->Apply(Shuffle2DSizes);
|
||||
BENCHMARK(BM_Shuffle3D)->Apply(Shuffle3DSizes);
|
||||
BENCHMARK(BM_Shuffle4D_NCHW_to_NHWC)->Apply(Shuffle4DSizes);
|
||||
80
unsupported/benchmarks/Tensor/bench_tensor_fft.cpp
Normal file
80
unsupported/benchmarks/Tensor/bench_tensor_fft.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// Benchmarks for Eigen Tensor FFT.
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
using namespace Eigen;
|
||||
|
||||
#ifndef SCALAR
|
||||
#define SCALAR float
|
||||
#endif
|
||||
|
||||
typedef SCALAR Scalar;
|
||||
|
||||
// --- 1D FFT ---
|
||||
static void BM_TensorFFT_1D(benchmark::State& state) {
|
||||
const int N = state.range(0);
|
||||
|
||||
Tensor<Scalar, 1> input(N);
|
||||
input.setRandom();
|
||||
|
||||
Eigen::array<int, 1> fft_dims = {0};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<std::complex<Scalar>, 1> result = input.template fft<BothParts, FFT_FORWARD>(fft_dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
double mflops = 5.0 * N * std::log2(static_cast<double>(N)) / 2.0; // real->complex
|
||||
state.counters["MFLOPS"] =
|
||||
benchmark::Counter(mflops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- 2D FFT ---
|
||||
static void BM_TensorFFT_2D(benchmark::State& state) {
|
||||
const int N = state.range(0);
|
||||
|
||||
Tensor<Scalar, 2> input(N, N);
|
||||
input.setRandom();
|
||||
|
||||
Eigen::array<int, 2> fft_dims = {0, 1};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<std::complex<Scalar>, 2> result = input.template fft<BothParts, FFT_FORWARD>(fft_dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
double total = N * N;
|
||||
double mflops = 5.0 * total * std::log2(static_cast<double>(N));
|
||||
state.counters["MFLOPS"] =
|
||||
benchmark::Counter(mflops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
// --- 1D inverse FFT ---
|
||||
static void BM_TensorIFFT_1D(benchmark::State& state) {
|
||||
const int N = state.range(0);
|
||||
|
||||
Tensor<std::complex<Scalar>, 1> input(N);
|
||||
input.setRandom();
|
||||
|
||||
Eigen::array<int, 1> fft_dims = {0};
|
||||
|
||||
for (auto _ : state) {
|
||||
Tensor<std::complex<Scalar>, 1> result = input.template fft<BothParts, FFT_REVERSE>(fft_dims);
|
||||
benchmark::DoNotOptimize(result.data());
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
double mflops = 5.0 * N * std::log2(static_cast<double>(N));
|
||||
state.counters["MFLOPS"] =
|
||||
benchmark::Counter(mflops, benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::kIs1000);
|
||||
}
|
||||
|
||||
static void FFTSizes(::benchmark::Benchmark* b) {
|
||||
for (int n : {64, 256, 1024, 4096}) {
|
||||
b->Arg(n);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_TensorFFT_1D)->Apply(FFTSizes);
|
||||
BENCHMARK(BM_TensorFFT_2D)->Apply(FFTSizes);
|
||||
BENCHMARK(BM_TensorIFFT_1D)->Apply(FFTSizes);
|
||||
Reference in New Issue
Block a user