Skip to content

Commit

Permalink
refactor visitor benchmarks
Browse files Browse the repository at this point in the history
  - move expressions to separate visitor_expressions.h header
  - add SYMENGINE_BENCHMARK_VISITORS macro to generate benchmarks for all expressions

add visitor_init benchmark to benchmark init() for
  - LambdaRealDoubleVisitor
  - LLVMDoubleVisitor
  - LLVMFloatVisitor

add more expressions
  - two large expressions based on symengine#1612
  - one expression copied from the llvm tests
  • Loading branch information
lkeegan committed Mar 22, 2021
1 parent 301b7a6 commit 6181f13
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 133 deletions.
4 changes: 4 additions & 0 deletions benchmarks/CMakeLists.txt
Expand Up @@ -82,8 +82,12 @@ endif()

if (BUILD_BENCHMARKS_GOOGLE)
find_package(benchmark REQUIRED)

add_executable(visitor_call visitor_call.cpp)
target_link_libraries(visitor_call symengine benchmark::benchmark)

add_executable(visitor_init visitor_init.cpp)
target_link_libraries(visitor_init symengine benchmark::benchmark)
endif()

add_executable(parsing parsing.cpp)
Expand Down
166 changes: 33 additions & 133 deletions benchmarks/visitor_call.cpp
@@ -1,173 +1,73 @@
#include <benchmark/benchmark.h>
#include <math.h>
#include <symengine/add.h>
#include <symengine/integer.h>
#include <symengine/lambda_double.h>
#include <symengine/llvm_double.h>
#include <symengine/mul.h>
#include <symengine/pow.h>
#include <symengine/symbol.h>
#include "visitor_expressions.h"

using SymEngine::add;
using SymEngine::Basic;
using SymEngine::cos;
using SymEngine::integer;
using SymEngine::LambdaRealDoubleVisitor;
using SymEngine::LLVMDoubleVisitor;
using SymEngine::LLVMFloatVisitor;
using SymEngine::mul;
using SymEngine::pow;
using SymEngine::RCP;
using SymEngine::sin;
using SymEngine::symbol;
using SymEngine::vec_basic;

struct Expr1 {
vec_basic vec{{symbol("x"), symbol("y"), symbol("z")}};
vec_basic expr()
{
vec_basic r{symbol("r0")};
r[0] = sin(add(vec[0],
cos(add(mul(vec[1], vec[2]), pow(vec[0], integer(2))))));
r[0] = mul(add(integer(3), r[0]), add(integer(2), r[0]));
r[0] = pow(add(integer(5), r[0]), add(integer(-2), r[0]));
return r;
}
static void compiled_expr(double *d, const double *v)
struct CompiledExpr1 {
void call(double *d, const double *v)
{
double r1
= std::sin(v[0] + std::cos((v[1] * v[2]) + std::pow(v[0], 2)));
double r2 = (3 + r1) * (2 + r1);
*d = std::pow((5 + r2), (r2 - 2));
}
static void compiled_expr(float *d, const float *v)
void call(float *d, const float *v)
{
float r1 = sinf(v[0] + cosf((v[1] * v[2]) + powf(v[0], 2)));
float r2 = (3 + r1) * (2 + r1);
*d = powf((5 + r2), (r2 - 2));
}
};

struct Expr2 {
vec_basic vec{{symbol("x"), symbol("y"), symbol("z")}};
vec_basic expr()
{
vec_basic r{symbol("r0"), symbol("r1"), symbol("r2")};
r[0] = mul(integer(2), add(vec[0], add(vec[0], mul(vec[1], vec[2]))));
r[1] = add(vec[0], add(vec[0], mul(vec[2], vec[1])));
r[2] = mul(integer(-2), add(vec[0], add(vec[0], mul(vec[1], vec[2]))));
return r;
}
struct CompiledExpr2 {
template <typename Real>
static void compiled_expr(Real *d, const Real *v)
void call(Real *d, const Real *v)
{
d[0] = 2.0 * (v[0] + v[0] + (v[1] * v[2]));
d[1] = v[0] + v[0] + (v[2] * v[1]);
d[2] = -2.0 * (v[0] + v[0] + (v[1] * v[2]));
}
};

template <typename Real>
struct NativeVisitor {
void (*f)(Real *, const Real *){nullptr};
void call(Real *d, const Real *v)
{
f(d, v);
}
};
void init(CompiledExpr1 &v, const vec_basic &args, const vec_basic &expr,
bool cse, unsigned opt_level){};

template <typename Expr, typename Real>
std::string init(NativeVisitor<Real> &v, bool cse, unsigned opt_level)
{
v.f = &Expr::compiled_expr;
return {};
}

template <typename Expr>
std::string init(LambdaRealDoubleVisitor &v, bool cse, unsigned opt_level)
{
std::string label;
Expr e;
v.init(e.vec, e.expr(), cse);
if (cse) {
label = "cse";
}
return label;
}

template <typename Expr>
std::string init(LLVMDoubleVisitor &v, bool cse, unsigned opt_level)
{
std::string label;
Expr e;
v.init(e.vec, e.expr(), cse, opt_level);
if (cse) {
label.append("cse_");
}
label.append("O");
label.append(std::to_string(opt_level));
return label;
}

template <typename Expr>
std::string init(LLVMFloatVisitor &v, bool cse, unsigned opt_level)
{
std::string label;
Expr e;
v.init(e.vec, e.expr(), cse, opt_level);
if (cse) {
label.append("cse_");
}
label.append("O");
label.append(std::to_string(opt_level));
return label;
}
void init(CompiledExpr2 &v, const vec_basic &args, const vec_basic &expr,
bool cse, unsigned opt_level){};

template <typename Visitor, typename Expr, typename Real>
static void Call(benchmark::State &state)
{
std::vector<Real> s{0.0, 0.0, 0.0};
std::vector<Real> d{0.0, 0.0, 0.0};
std::vector<Real> x{1.0, 4.4365, 12.8};
Expr e;
vec_basic inputs{e.vec};
vec_basic outputs{e.expr()};
const std::size_t n_inputs{inputs.size()};
const std::size_t n_outputs{outputs.size()};
std::vector<Real> s(n_outputs, 0.0);
std::vector<Real> d(n_outputs, 0.0);
std::vector<Real> x(n_inputs, 0.0);
for (std::size_t i = 0; i < n_inputs; ++i) {
x[i] = 1.732 * static_cast<double>(i);
}
Visitor v;
bool cse{static_cast<bool>(state.range(0))};
unsigned opt_level{static_cast<unsigned>(state.range(1))};
auto label = init<Expr>(v, cse, opt_level);
init(v, inputs, outputs, cse, opt_level);
for (auto _ : state) {
x[0] += 0.1;
x[1] += 0.2;
x[2] += 0.3;
for (std::size_t i = 0; i < n_inputs; ++i) {
x[i] += 0.1;
}
v.call(d.data(), x.data());
benchmark::ClobberMemory();
s[0] += d[0];
s[1] += d[1];
s[2] += d[2];
for (std::size_t i = 0; i < n_outputs; ++i) {
s[i] += d[i];
}
}
state.SetLabel(label);
state.SetLabel(to_label(cse, opt_level));
}

// BENCHMARK_TEMPLATE(BenchmarkName, VisitorClass,
// ExpressionClass, RealType)->ArgsProduct({{cse values}, {opt_level values}});

static std::vector<int64_t> opt_code_values{0, 1, 2, 3};
static std::vector<int64_t> cse_values{0, 1};

BENCHMARK_TEMPLATE(Call, LambdaRealDoubleVisitor, Expr1, double)
->ArgsProduct({cse_values, {0}});
BENCHMARK_TEMPLATE(Call, LLVMDoubleVisitor, Expr1, double)
->ArgsProduct({cse_values, opt_code_values});
BENCHMARK_TEMPLATE(Call, NativeVisitor<double>, Expr1, double)->Args({0, 0});
BENCHMARK_TEMPLATE(Call, LLVMFloatVisitor, Expr1, float)
->ArgsProduct({cse_values, opt_code_values});
BENCHMARK_TEMPLATE(Call, NativeVisitor<float>, Expr1, float)->Args({0, 0});
SYMENGINE_BENCHMARK_VISITORS(Call);

BENCHMARK_TEMPLATE(Call, LambdaRealDoubleVisitor, Expr2, double)
->ArgsProduct({cse_values, {0}});
BENCHMARK_TEMPLATE(Call, LLVMDoubleVisitor, Expr2, double)
->ArgsProduct({cse_values, opt_code_values});
BENCHMARK_TEMPLATE(Call, NativeVisitor<double>, Expr2, double)->Args({0, 0});
BENCHMARK_TEMPLATE(Call, LLVMFloatVisitor, Expr2, float)
->ArgsProduct({cse_values, opt_code_values});
BENCHMARK_TEMPLATE(Call, NativeVisitor<float>, Expr2, float)->Args({0, 0});
// repeat benchmarks with natively compiled version of expressions
BENCHMARK_TEMPLATE(Call, CompiledExpr1, Expr1, double)->Args({0, 0});
BENCHMARK_TEMPLATE(Call, CompiledExpr2, Expr2, double)->Args({0, 0});

BENCHMARK_MAIN();

0 comments on commit 6181f13

Please sign in to comment.