diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 970f64c4f2..cfdc872f36 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -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) diff --git a/benchmarks/visitor_call.cpp b/benchmarks/visitor_call.cpp index 7abbcf5232..746b2e11a9 100644 --- a/benchmarks/visitor_call.cpp +++ b/benchmarks/visitor_call.cpp @@ -1,46 +1,15 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include +#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); @@ -48,18 +17,9 @@ struct Expr1 { } }; -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 - 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]); @@ -67,107 +27,47 @@ struct Expr2 { } }; -template -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 -std::string init(NativeVisitor &v, bool cse, unsigned opt_level) -{ - v.f = &Expr::compiled_expr; - return {}; -} - -template -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 -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 -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 static void Call(benchmark::State &state) { - std::vector s{0.0, 0.0, 0.0}; - std::vector d{0.0, 0.0, 0.0}; - std::vector 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 s(n_outputs, 0.0); + std::vector d(n_outputs, 0.0); + std::vector x(n_inputs, 0.0); + for (std::size_t i = 0; i < n_inputs; ++i) { + x[i] = 1.732 * static_cast(i); + } Visitor v; bool cse{static_cast(state.range(0))}; unsigned opt_level{static_cast(state.range(1))}; - auto label = init(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 opt_code_values{0, 1, 2, 3}; -static std::vector 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, Expr1, double)->Args({0, 0}); -BENCHMARK_TEMPLATE(Call, LLVMFloatVisitor, Expr1, float) - ->ArgsProduct({cse_values, opt_code_values}); -BENCHMARK_TEMPLATE(Call, NativeVisitor, 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, Expr2, double)->Args({0, 0}); -BENCHMARK_TEMPLATE(Call, LLVMFloatVisitor, Expr2, float) - ->ArgsProduct({cse_values, opt_code_values}); -BENCHMARK_TEMPLATE(Call, NativeVisitor, 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(); diff --git a/benchmarks/visitor_expressions.h b/benchmarks/visitor_expressions.h new file mode 100644 index 0000000000..73b10edd87 --- /dev/null +++ b/benchmarks/visitor_expressions.h @@ -0,0 +1,205 @@ +#ifndef SYMENGINE_BENCHMARKS_VISITOR_EXPRESSIONS_H +#define SYMENGINE_BENCHMARKS_VISITOR_EXPRESSIONS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using SymEngine::add; +using SymEngine::Basic; +using SymEngine::cos; +using SymEngine::DenseMatrix; +using SymEngine::E; +using SymEngine::integer; +using SymEngine::LambdaRealDoubleVisitor; +using SymEngine::LLVMDoubleVisitor; +using SymEngine::LLVMFloatVisitor; +using SymEngine::log; +using SymEngine::mul; +using SymEngine::pi; +using SymEngine::pow; +using SymEngine::RCP; +using SymEngine::sin; +using SymEngine::sqrt; +using SymEngine::symbol; +using SymEngine::vec_basic; + +static const std::vector opt_code_values{0, 1, 2, 3}; +static const std::vector cse_values{0, 1}; + +std::string to_label(bool cse, unsigned opt_level) +{ + std::string label; + if (cse) { + label.append("cse_"); + } + label.append("O"); + label.append(std::to_string(opt_level)); + return label; +} + +#define SYMENGINE_BENCHMARK_VISITORS_EXPR(func, expr) \ + BENCHMARK_TEMPLATE(func, LambdaRealDoubleVisitor, expr, double) \ + ->ArgsProduct({cse_values, {0}}); \ + BENCHMARK_TEMPLATE(func, LLVMDoubleVisitor, expr, double) \ + ->ArgsProduct({cse_values, opt_code_values}); \ + BENCHMARK_TEMPLATE(func, LLVMFloatVisitor, expr, float) \ + ->ArgsProduct({cse_values, opt_code_values}) + +// to add an expression to these benchmarks: +// - add a line to the macro below with your expression ExprN +// - define a struct ExprN that implements vec and expr() + +#define SYMENGINE_BENCHMARK_VISITORS(func) \ + SYMENGINE_BENCHMARK_VISITORS_EXPR(func, Expr1); \ + SYMENGINE_BENCHMARK_VISITORS_EXPR(func, Expr2); \ + SYMENGINE_BENCHMARK_VISITORS_EXPR(func, Expr3); \ + SYMENGINE_BENCHMARK_VISITORS_EXPR(func, Expr4); \ + SYMENGINE_BENCHMARK_VISITORS_EXPR(func, Expr5) + +void init(LLVMFloatVisitor &v, const vec_basic &args, const vec_basic &expr, + bool cse, unsigned opt_level) +{ + v.init(args, expr, cse, opt_level); +} + +void init(LLVMDoubleVisitor &v, const vec_basic &args, const vec_basic &expr, + bool cse, unsigned opt_level) +{ + v.init(args, expr, cse, opt_level); +} + +void init(LambdaRealDoubleVisitor &v, const vec_basic &args, + const vec_basic &expr, bool cse, unsigned opt_level) +{ + v.init(args, expr, cse); +} + +// each expression struct implements +// - vec: a vector of inputs +// - expr(): a vector of outputs that can depend on the inputs + +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; + } +}; + +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; + } +}; + +// copied from llvm_double test case +struct Expr3 { + vec_basic vec{{symbol("x"), symbol("y"), symbol("z")}}; + vec_basic expr() + { + vec_basic r{symbol("r0")}; + auto &x = vec[0]; + auto &y = vec[1]; + auto &z = vec[2]; + vec_basic v = {log(x), + abs(x), + tan(x), + sinh(x), + cosh(x), + tanh(x), + asinh(y), + acosh(y), + atanh(x), + asin(x), + acos(x), + atan(x), + gamma(x), + loggamma(x), + erf(x), + erfc(x), + add(pi, div(integer(1), integer(3)))}; + r[0] = mul(add(sin(x), add(mul(pow(y, integer(4)), mul(z, integer(2))), + pow(sin(x), integer(2)))), + add(v)); + for (int i = 0; i < 4; ++i) { + r[0] = mul( + add(pow(integer(2), E), add(r[0], pow(x, pow(E, cos(x))))), + r[0]); + } + return r; + } +}; + +// large cse-friendly expression based on +// https://github.com/symengine/symengine/pull/1612 +struct Expr4 { + vec_basic vec{symbol("a"), symbol("b"), symbol("c"), + symbol("d"), symbol("e"), symbol("f")}; + vec_basic expr() + { + RCP e = integer(23); + const std::size_t n{vec.size()}; + for (std::size_t i = 0; i < n; ++i) { + e = pow(e, + add(cos(sqrt(log(sin(pow(vec[n - i - 1], vec[i]))))), e)); + } + e = expand(e); + DenseMatrix M(1, 1, {e}); + DenseMatrix S(n, 1, vec); + DenseMatrix J(1, n); + jacobian(M, S, J); + vec_basic expression; + for (std::size_t i = 0; i < n; ++i) { + expression.push_back(J.get(0, i)); + } + return expression; + } +}; + +// large cse-friendly expression based on +// https://github.com/symengine/symengine/pull/1612 +struct Expr5 { + vec_basic vec{symbol("a"), symbol("b"), symbol("c"), + symbol("d"), symbol("e"), symbol("f")}; + vec_basic expr() + { + RCP e = integer(23); + const std::size_t n{vec.size()}; + for (std::size_t i = 0; i < n; ++i) { + e = pow(e, cos(sqrt(log(sin(pow(vec[n - i - 1], vec[i])))))); + } + e = expand(e); + DenseMatrix M(1, 1, {e}); + DenseMatrix S(n, 1, vec); + DenseMatrix J(1, n); + jacobian(M, S, J); + vec_basic expression; + for (std::size_t i = 0; i < n; ++i) { + expression.push_back(J.get(0, i)); + } + return expression; + } +}; + +#endif \ No newline at end of file diff --git a/benchmarks/visitor_init.cpp b/benchmarks/visitor_init.cpp new file mode 100644 index 0000000000..7a599bf97f --- /dev/null +++ b/benchmarks/visitor_init.cpp @@ -0,0 +1,21 @@ +#include +#include "visitor_expressions.h" + +template +static void Init(benchmark::State &state) +{ + bool cse{static_cast(state.range(0))}; + unsigned opt_level{static_cast(state.range(1))}; + Expr e; + vec_basic inputs{e.vec}; + vec_basic outputs{e.expr()}; + Visitor v; + for (auto _ : state) { + init(v, inputs, outputs, cse, opt_level); + } + state.SetLabel(to_label(cse, opt_level)); +} + +SYMENGINE_BENCHMARK_VISITORS(Init); + +BENCHMARK_MAIN();