Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add benchmark for expression templates #634

Merged
merged 2 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions benchmark/ArrayExpressionTemplates.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "benchmark/benchmark.h"

#include "clad/Differentiator/Differentiator.h"

// Benchmark the expression x*y + y*z + z*x between clad arrays,
// this is to compare the performance of expression templates.
// We will evaluate the expression on using four different methods:
// 1. Using operations on clad arrays - this will use expression templates.
// 2. Using clad arrays but creating temporaries manually.
// 3. Using loops on clad arrays.
// 4. Using loops on native arrays.

// Benchmark expression templates.
static void BM_ExpressionTemplates(benchmark::State& state) {
constexpr int n = 1000;
clad::array<double> x(n);
clad::array<double> y(n);
clad::array<double> z(n);
for (int i = 0; i < n; ++i) {
x[i] = i + 1;
y[i] = i + 2;
z[i] = i + 3;
}

clad::array<double> res(n);
for (auto _ : state)
benchmark::DoNotOptimize(res = x * y + y * z + z * x);
}
BENCHMARK(BM_ExpressionTemplates);

// Benchmark manually creating temporaries.
static void BM_ManualTemporaries(benchmark::State& state) {
constexpr int n = 1000;
clad::array<double> x(n);
clad::array<double> y(n);
clad::array<double> z(n);
for (int i = 0; i < n; ++i) {
x[i] = i + 1;
y[i] = i + 2;
z[i] = i + 3;
}

clad::array<double> res(n);
for (auto _ : state) {
clad::array<double> temp1 = x * y;
clad::array<double> temp2 = y * z;
clad::array<double> temp3 = z * x;
clad::array<double> temp4 = temp1 + temp2;
benchmark::DoNotOptimize(res = temp4 + temp3);
}
}
BENCHMARK(BM_ManualTemporaries);

// Benchmark loops on clad arrays.
static void BM_LoopsOnCladArrays(benchmark::State& state) {
constexpr int n = 1000;
clad::array<double> x(n);
clad::array<double> y(n);
clad::array<double> z(n);
for (int i = 0; i < n; ++i) {
x[i] = i + 1;
y[i] = i + 2;
z[i] = i + 3;
}

clad::array<double> res(n);
for (auto _ : state) {
for (int i = 0; i < n; ++i) {
benchmark::DoNotOptimize(res[i] =
x[i] * y[i] + y[i] * z[i] + z[i] * x[i]);
}
}
}
BENCHMARK(BM_LoopsOnCladArrays);

// Benchmark loops on native arrays.
static void BM_LoopsOnNativeArrays(benchmark::State& state) {
constexpr int n = 1000;
double* x = new double[n];
double* y = new double[n];
double* z = new double[n];
for (int i = 0; i < n; ++i) {
x[i] = i + 1;
y[i] = i + 2;
z[i] = i + 3;
}

double* res = new double[n];
for (auto _ : state) {
for (int i = 0; i < n; ++i) {
benchmark::DoNotOptimize(res[i] =
x[i] * y[i] + y[i] * z[i] + z[i] * x[i]);
}
}

delete[] x;
delete[] y;
delete[] z;
delete[] res;
}
BENCHMARK(BM_LoopsOnNativeArrays);

// Define our main.
BENCHMARK_MAIN();
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include(AddCladBenchmark)

CB_ADD_GBENCHMARK(Simple Simple.cpp)
CB_ADD_GBENCHMARK(AlgorithmicComplexity AlgorithmicComplexity.cpp)
CB_ADD_GBENCHMARK(ArrayExpressionTemplates ArrayExpressionTemplates.cpp)
CB_ADD_GBENCHMARK(EnzymeCladComparison EnzymeCladComparison.cpp)
CB_ADD_GBENCHMARK(MemoryComplexity MemoryComplexity.cpp)
CB_ADD_GBENCHMARK(VectorModeComparison VectorModeComparison.cpp)
Expand Down
69 changes: 49 additions & 20 deletions include/clad/Differentiator/Array.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ template <typename T> class array {
m_arr[i] = expression[i];
}

template <typename L, typename BinaryOp, typename R>
CUDA_HOST_DEVICE array(const array_expression<L, BinaryOp, R>& expression)
: m_arr(new T[expression.size()]), m_size(expression.size()) {
for (std::size_t i = 0; i < expression.size(); ++i)
m_arr[i] = expression[i];
}

// initializing all entries using the same value
template <typename U>
CUDA_HOST_DEVICE array(std::size_t size, U val)
Expand Down Expand Up @@ -293,17 +300,19 @@ template <typename T> class array {
}

/// Negate the array and return a new array.
CUDA_HOST_DEVICE array_expression<T, BinarySub, array<T>> operator-() const {
return array_expression<T, BinarySub, array<T>>(static_cast<T>(0), *this);
CUDA_HOST_DEVICE array_expression<T, BinarySub, array<T> const&>
operator-() const {
return array_expression<T, BinarySub, array<T> const&>(static_cast<T>(0),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return array_expression<T, BinarySub, array<T> const&>(static_cast<T>(0),
return array_expression<T, BinarySub, const array<T>&>(static_cast<T>(0),

We spell const in clad this way. Can you update the rest of the occurrences?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed and also added to .clang-format

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

*this);
}

/// Subtracts the number from every element in the array and returns a new
/// array, when the number is on the left side.
template <typename U, typename std::enable_if<std::is_arithmetic<U>::value,
int>::type = 0>
CUDA_HOST_DEVICE friend array_expression<U, BinarySub, array<T>>
CUDA_HOST_DEVICE friend array_expression<U, BinarySub, array<T> const&>
operator-(U n, const array<T>& arr) {
return array_expression<U, BinarySub, array<T>>(n, arr);
return array_expression<U, BinarySub, array<T> const&>(n, arr);
}

/// Implicitly converts from clad::array to pointer to an array of type T
Expand Down Expand Up @@ -333,69 +342,89 @@ template <typename T> CUDA_HOST_DEVICE array<T> zero_vector(std::size_t n) {
/// expression.
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryMul, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryMul, U>
operator*(const array<T>& arr, U n) {
return array_expression<array<T>, BinaryMul, U>(arr, n);
return array_expression<array<T> const&, BinaryMul, U>(arr, n);
}

/// Multiplies the number to every element in the array and returns an array
/// expression, when the number is on the left side.
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryMul, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryMul, U>
operator*(U n, const array<T>& arr) {
return array_expression<array<T>, BinaryMul, U>(arr, n);
return array_expression<array<T> const&, BinaryMul, U>(arr, n);
}

/// Divides the number from every element in the array and returns an array
/// expression.
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryDiv, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryDiv, U>
operator/(const array<T>& arr, U n) {
return array_expression<array<T>, BinaryDiv, U>(arr, n);
return array_expression<array<T> const&, BinaryDiv, U>(arr, n);
}

/// Adds the number to every element in the array and returns a new array
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryAdd, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryAdd, U>
operator+(const array<T>& arr, U n) {
return array_expression<array<T>, BinaryAdd, U>(arr, n);
return array_expression<array<T> const&, BinaryAdd, U>(arr, n);
}

/// Adds the number to every element in the array and returns an array
/// expression, when the number is on the left side.
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryAdd, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryAdd, U>
operator+(U n, const array<T>& arr) {
return array_expression<array<T>, BinaryAdd, U>(arr, n);
return array_expression<array<T> const&, BinaryAdd, U>(arr, n);
}

/// Subtracts the number from every element in the array and returns an array
/// expression.
template <typename T, typename U,
typename std::enable_if<std::is_arithmetic<U>::value, int>::type = 0>
CUDA_HOST_DEVICE array_expression<array<T>, BinarySub, U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinarySub, U>
operator-(const array<T>& arr, U n) {
return array_expression<array<T>, BinarySub, U>(arr, n);
return array_expression<array<T> const&, BinarySub, U>(arr, n);
}

/// Function to define element wise adding of two arrays.
template <typename T, typename U>
CUDA_HOST_DEVICE array_expression<array<T>, BinaryAdd, array<U>>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryAdd, array<U> const&>
operator+(const array<T>& arr1, const array<U>& arr2) {
assert(arr1.size() == arr2.size());
return array_expression<array<T>, BinaryAdd, array<U>>(arr1, arr2);
return array_expression<array<T> const&, BinaryAdd, array<U> const&>(arr1,
arr2);
}

/// Function to define element wise subtraction of two arrays.
template <typename T, typename U>
CUDA_HOST_DEVICE array_expression<array<T>, BinarySub, array<U>>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinarySub, array<U> const&>
operator-(const array<T>& arr1, const array<U>& arr2) {
assert(arr1.size() == arr2.size());
return array_expression<array<T>, BinarySub, array<U>>(arr1, arr2);
return array_expression<array<T> const&, BinarySub, array<U> const&>(arr1,
arr2);
}

/// Function to define element wise multiplication of two arrays.
template <typename T, typename U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryMul, array<U> const&>
operator*(const array<T>& arr1, const array<U>& arr2) {
assert(arr1.size() == arr2.size());
return array_expression<array<T> const&, BinaryMul, array<U> const&>(arr1,
arr2);
}

/// Function to define element wise division of two arrays.
template <typename T, typename U>
CUDA_HOST_DEVICE array_expression<array<T> const&, BinaryDiv, array<U> const&>
operator/(const array<T>& arr1, const array<U>& arr2) {
assert(arr1.size() == arr2.size());
return array_expression<array<T> const&, BinaryDiv, array<U> const&>(arr1,
arr2);
}

} // namespace clad
Expand Down
52 changes: 33 additions & 19 deletions include/clad/Differentiator/ArrayExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class array_expression {
RightExp r;

public:
array_expression(LeftExp const& l, RightExp const& r) : l(l), r(r) {}
array_expression(LeftExp l, RightExp r) : l(l), r(r) {}

// for scalars
template <typename T, typename std::enable_if<std::is_arithmetic<T>::value,
Expand Down Expand Up @@ -84,65 +84,79 @@ class array_expression {

// Operator overload for addition.
template <typename RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp>, BinaryAdd, RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp> const&,
BinaryAdd, RE>
operator+(RE const& r) const {
return array_expression<array_expression<LeftExp, BinaryOp, RightExp>,
BinaryAdd, RE>(*this, r);
return array_expression<
array_expression<LeftExp, BinaryOp, RightExp> const&, BinaryAdd, RE>(
*this, r);
}

// Operator overload for multiplication.
template <typename RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp>, BinaryMul, RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp> const&,
BinaryMul, RE>
operator*(RE const& r) const {
return array_expression<array_expression<LeftExp, BinaryOp, RightExp>,
BinaryMul, RE>(*this, r);
return array_expression<
array_expression<LeftExp, BinaryOp, RightExp> const&, BinaryMul, RE>(
*this, r);
}

// Operator overload for subtraction.
template <typename RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp>, BinarySub, RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp> const&,
BinarySub, RE>
operator-(RE const& r) const {
return array_expression<array_expression<LeftExp, BinaryOp, RightExp>,
BinarySub, RE>(*this, r);
return array_expression<
array_expression<LeftExp, BinaryOp, RightExp> const&, BinarySub, RE>(
*this, r);
}

// Operator overload for division.
template <typename RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp>, BinaryDiv, RE>
array_expression<array_expression<LeftExp, BinaryOp, RightExp> const&,
BinaryDiv, RE>
operator/(RE const& r) const {
return array_expression<array_expression<LeftExp, BinaryOp, RightExp>,
BinaryDiv, RE>(*this, r);
return array_expression<
array_expression<LeftExp, BinaryOp, RightExp> const&, BinaryDiv, RE>(
*this, r);
}
};

// Operator overload for addition, when the right operand is an array_expression
// and the left operand is a scalar.
template <typename T, typename LeftExp, typename BinaryOp, typename RightExp,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
array_expression<T, BinaryAdd, array_expression<LeftExp, BinaryOp, RightExp>>
array_expression<T, BinaryAdd,
array_expression<LeftExp, BinaryOp, RightExp> const&>
operator+(T const& l, array_expression<LeftExp, BinaryOp, RightExp> const& r) {
return array_expression<T, BinaryAdd,
array_expression<LeftExp, BinaryOp, RightExp>>(l, r);
array_expression<LeftExp, BinaryOp, RightExp> const&>(
l, r);
}

// Operator overload for multiplication, when the right operand is an
// array_expression and the left operand is a scalar.
template <typename T, typename LeftExp, typename BinaryOp, typename RightExp,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
array_expression<T, BinaryMul, array_expression<LeftExp, BinaryOp, RightExp>>
array_expression<T, BinaryMul,
array_expression<LeftExp, BinaryOp, RightExp> const&>
operator*(T const& l, array_expression<LeftExp, BinaryOp, RightExp> const& r) {
return array_expression<T, BinaryMul,
array_expression<LeftExp, BinaryOp, RightExp>>(l, r);
array_expression<LeftExp, BinaryOp, RightExp> const&>(
l, r);
}

// Operator overload for subtraction, when the right operand is an
// array_expression and the left operand is a scalar.
template <typename T, typename LeftExp, typename BinaryOp, typename RightExp,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
array_expression<T, BinarySub, array_expression<LeftExp, BinaryOp, RightExp>>
array_expression<T, BinarySub,
array_expression<LeftExp, BinaryOp, RightExp> const&>
operator-(T const& l, array_expression<LeftExp, BinaryOp, RightExp> const& r) {
return array_expression<T, BinarySub,
array_expression<LeftExp, BinaryOp, RightExp>>(l, r);
array_expression<LeftExp, BinaryOp, RightExp> const&>(
l, r);
}
} // namespace clad
// NOLINTEND(*-pointer-arithmetic)
Expand Down
Loading
Loading