Skip to content

Commit

Permalink
Addition with saturation + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Epixu committed Feb 1, 2025
1 parent 0d77026 commit 1e67ae3
Show file tree
Hide file tree
Showing 8 changed files with 592 additions and 149 deletions.
253 changes: 212 additions & 41 deletions source/binary/Add.hpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion source/binary/Subtract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Langulus::SIMD
}

/// Subtract two registers
/// @tparam SATURATE - whether to clamp to max if overflow occurs
/// @tparam SATURATE - whether to clamp to [min;max]
/// @param lhs - left register
/// @param rhs - right register
/// @return the resulting register
Expand Down
166 changes: 166 additions & 0 deletions test/Add/TestAdd-VS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
///
/// Langulus::SIMD
/// Copyright (c) 2019 Dimo Markov <team@langulus.com>
/// Part of the Langulus framework, see https://langulus.com
///
/// SPDX-License-Identifier: MIT
///
#include "TestAdd.hpp"


TEMPLATE_TEST_CASE("Vector + Scalar", "[add]"
, NUMBERS_ALL()
, VECTORS_ALL(1)
, VECTORS_ALL(2)
, VECTORS_ALL(3)
, VECTORS_ALL(4)
, VECTORS_ALL(5)
, VECTORS_ALL(8)
, VECTORS_ALL(9)
, VECTORS_ALL(16)
, VECTORS_ALL(17)
, VECTORS_ALL(32)
, VECTORS_ALL(33)
) {
using T = TestType;
using E = TypeOf<T>;
static_assert(CountOf<Vector<signed char, 2>> == 2);

GIVEN("Vector<T,N> + Scalar<T> = Vector<T,N>") {
T x;
E y {};
T r, rCheck;

if constexpr (not CT::Vector<T>) {
InitOne(x, 1);
InitOne(y, -5);
}
else InitOne(y, -5);

WHEN("Added as constexpr (with saturation)") {
constexpr T lhs = E {0};
constexpr E rhs = E {5};
static_assert(SIMD::Add<true>(lhs, rhs) == T {CT::Real<TypeOf<T>> ? 1 : 5});
}

WHEN("Added as constexpr (without saturation)") {
constexpr T lhs = E {0};
constexpr E rhs = E {5};
static_assert(SIMD::Add<false>(lhs, rhs) == static_cast<T>(5));
}

WHEN("Added (with saturation)") {
ControlAdd<true>(x, y, rCheck);
SIMD::Add<true>(x, y, r);

REQUIRE(r == rCheck);

#ifdef LANGULUS_STD_BENCHMARK
BENCHMARK_ADVANCED("Add (control)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
ControlAdd(nx[i], ny[i], nr[i]);
});
};

BENCHMARK_ADVANCED("Add (SIMD)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
if constexpr (CT::Vector<T>)
SIMD::Add(nx[i].mArray, ny[i].mArray, nr[i].mArray);
else
SIMD::Add(nx[i], ny[i], nr[i]);
});
};
#endif
}

WHEN("Added (without saturation)") {
ControlAdd<false>(x, y, rCheck);
SIMD::Add<false>(x, y, r);

REQUIRE(r == rCheck);

#ifdef LANGULUS_STD_BENCHMARK
BENCHMARK_ADVANCED("Add (control)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
ControlAdd(nx[i], ny[i], nr[i]);
});
};

BENCHMARK_ADVANCED("Add (SIMD)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
if constexpr (CT::Vector<T>)
SIMD::Add(nx[i].mArray, ny[i].mArray, nr[i].mArray);
else
SIMD::Add(nx[i], ny[i], nr[i]);
});
};
#endif
}

WHEN("Added in reverse (with saturation)") {
ControlAdd<true>(y, x, rCheck);
SIMD::Add<true>(y, x, r);

REQUIRE(r == rCheck);
}

WHEN("Added in reverse (without saturation)") {
ControlAdd<false>(y, x, rCheck);
SIMD::Add<false>(y, x, r);

REQUIRE(r == rCheck);
}
}
}
162 changes: 162 additions & 0 deletions test/Add/TestAdd-VV.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
///
/// Langulus::SIMD
/// Copyright (c) 2019 Dimo Markov <team@langulus.com>
/// Part of the Langulus framework, see https://langulus.com
///
/// SPDX-License-Identifier: MIT
///
#include "TestAdd.hpp"


TEMPLATE_TEST_CASE("Vector + Vector", "[add]"
, NUMBERS_ALL()
, VECTORS_ALL(1)
, VECTORS_ALL(2)
, VECTORS_ALL(3)
, VECTORS_ALL(4)
, VECTORS_ALL(5)
, VECTORS_ALL(8)
, VECTORS_ALL(9)
, VECTORS_ALL(16)
, VECTORS_ALL(17)
, VECTORS_ALL(32)
, VECTORS_ALL(33)
) {
using T = TestType;

GIVEN("x * y = r") {
T x, y;
T r, rCheck;

if constexpr (not CT::Vector<T>) {
InitOne(x, 1);
InitOne(y, -5);
}

WHEN("Added as constexpr (with saturation)") {
constexpr T lhs {0};
constexpr T rhs {5};
static_assert(SIMD::Add<true>(lhs, rhs) == T {CT::Real<TypeOf<T>> ? 1 : 5});
}

WHEN("Added as constexpr (without saturation)") {
constexpr T lhs {0};
constexpr T rhs {5};
static_assert(SIMD::Add<false>(lhs, rhs) == static_cast<T>(5));
}

WHEN("Added (with saturation)") {
ControlAdd<true>(x, y, rCheck);
SIMD::Add<true>(x, y, r);

REQUIRE(r == rCheck);

#ifdef LANGULUS_STD_BENCHMARK
BENCHMARK_ADVANCED("Add (control)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
ControlAdd(nx[i], ny[i], nr[i]);
});
};

BENCHMARK_ADVANCED("Add (SIMD)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
if constexpr (CT::Vector<T>)
SIMD::Add(nx[i].mArray, ny[i].mArray, nr[i].mArray);
else
SIMD::Add(nx[i], ny[i], nr[i]);
});
};
#endif
}

WHEN("Added (without saturation)") {
ControlAdd<false>(x, y, rCheck);
SIMD::Add<false>(x, y, r);

REQUIRE(r == rCheck);

#ifdef LANGULUS_STD_BENCHMARK
BENCHMARK_ADVANCED("Add (control)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
ControlAdd(nx[i], ny[i], nr[i]);
});
};

BENCHMARK_ADVANCED("Add (SIMD)") (timer meter) {
some<T> nx(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : nx)
InitOne(i, 1);
}

some<T> ny(meter.runs());
if constexpr (not CT::Vector<T>) {
for (auto& i : ny)
InitOne(i, 1);
}

some<T> nr(meter.runs());
meter.measure([&](int i) {
if constexpr (CT::Vector<T>)
SIMD::Add(nx[i].mArray, ny[i].mArray, nr[i].mArray);
else
SIMD::Add(nx[i], ny[i], nr[i]);
});
};
#endif
}

WHEN("Added in reverse (with saturation)") {
ControlAdd<true>(y, x, rCheck);
SIMD::Add<true>(y, x, r);

REQUIRE(r == rCheck);
}

WHEN("Added in reverse (without saturation)") {
ControlAdd<false>(y, x, rCheck);
SIMD::Add<false>(y, x, r);

REQUIRE(r == rCheck);
}
}
}
51 changes: 51 additions & 0 deletions test/Add/TestAdd.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
///
/// Langulus::SIMD
/// Copyright (c) 2019 Dimo Markov <team@langulus.com>
/// Part of the Langulus framework, see https://langulus.com
///
/// SPDX-License-Identifier: MIT
///
#pragma once
#include "../Common.hpp"


/// Scalar + Scalar (either dense or sparse, wrapped or not)
template<bool SATURATE, CT::Scalar LHS, CT::Scalar RHS, CT::Scalar OUT> LANGULUS(INLINED)
void ControlAdd(const LHS& lhs, const RHS& rhs, OUT& out) noexcept {
auto& fout = FundamentalCast(out);
fout = SIMD::Inner::AddFallback<SATURATE>(FundamentalCast(lhs), FundamentalCast(rhs));
}

/// Vector + Vector (either dense or sparse, wrapped or not)
template<bool SATURATE, CT::Vector LHS, CT::Vector RHS, CT::Vector OUT> LANGULUS(INLINED)
void ControlAdd(const LHS& lhsArray, const RHS& rhsArray, OUT& out) noexcept {
static_assert(LHS::MemberCount == RHS::MemberCount
and LHS::MemberCount == OUT::MemberCount,
"Vector sizes must match");

auto r = out.mArray;
auto lhs = lhsArray.mArray;
auto rhs = rhsArray.mArray;
const auto lhsEnd = lhs + LHS::MemberCount;
while (lhs != lhsEnd)
ControlAdd<SATURATE>(*lhs++, *rhs++, *r++);
}

/// Scalar + Vector (either dense or sparse, wrapped or not)
template<bool SATURATE, CT::Scalar LHS, CT::Vector RHS, CT::Vector OUT> LANGULUS(INLINED)
void ControlAdd(const LHS& lhs, const RHS& rhsArray, OUT& out) noexcept {
static_assert(RHS::MemberCount == OUT::MemberCount,
"Vector sizes must match");

auto r = out.mArray;
auto rhs = rhsArray.mArray;
const auto rhsEnd = rhs + RHS::MemberCount;
while (rhs != rhsEnd)
ControlAdd<SATURATE>(lhs, *rhs++, *r++);
}

/// Vector + Scalar (either dense or sparse, wrapped or not)
template<bool SATURATE, CT::Vector LHS, CT::Scalar RHS, CT::Vector OUT> LANGULUS(INLINED)
void ControlAdd(const LHS& lhsArray, const RHS& rhs, OUT& out) noexcept {
return ControlAdd<SATURATE>(rhs, lhsArray, out);
}
Loading

0 comments on commit 1e67ae3

Please sign in to comment.