Add an allWithin SIMD helper
This commit is contained in:
parent
ff8ac64d61
commit
c034fd3e30
8 changed files with 180 additions and 2 deletions
70
benchmarks/BM_allWithin.cpp
Normal file
70
benchmarks/BM_allWithin.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "SIMDHelpers.h"
|
||||
#include "Macros.h"
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
class WithinArray : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 1, 10 };
|
||||
input = std::vector<float>(state.range(0));
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state) {
|
||||
UNUSED(state);
|
||||
}
|
||||
|
||||
std::vector<float> input;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(WithinArray, ScalarFalse)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, false);
|
||||
sfz::allWithin<float>(input, 1.2f, 3.8f);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(WithinArray, SIMDFalse)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, true);
|
||||
sfz::allWithin<float>(input, 1.2f, 3.8f);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(WithinArray, ScalarTrue)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, false);
|
||||
sfz::allWithin<float>(input, 0.0f, 11.0f);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(WithinArray, SIMDTrue)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, true);
|
||||
sfz::allWithin<float>(input, 0.0f, 11.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BENCHMARK_REGISTER_F(WithinArray, ScalarFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(WithinArray, SIMDFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(WithinArray, ScalarTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(WithinArray, SIMDTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -60,6 +60,7 @@ target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
|
|||
sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp)
|
||||
sfizz_add_benchmark(bm_random BM_random.cpp)
|
||||
sfizz_add_benchmark(bm_clamp BM_clamp.cpp)
|
||||
sfizz_add_benchmark(bm_allWithin BM_allWithin.cpp)
|
||||
|
||||
sfizz_add_benchmark(bm_logger BM_logger.cpp)
|
||||
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ struct SIMDDispatch {
|
|||
decltype(&meanScalar<T>) mean = &meanScalar<T>;
|
||||
decltype(&meanSquaredScalar<T>) meanSquared = &meanSquaredScalar<T>;
|
||||
decltype(&clampAllScalar<T>) clampAll = &clampAllScalar<T>;
|
||||
decltype(&allWithinScalar<T>) allWithin = &allWithinScalar<T>;
|
||||
|
||||
private:
|
||||
std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
|
||||
|
|
@ -86,6 +87,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
|
|||
SIMD_OP(mean)
|
||||
SIMD_OP(meanSquared)
|
||||
SIMD_OP(clampAll)
|
||||
SIMD_OP(allWithin)
|
||||
}
|
||||
#undef SIMD_OP
|
||||
}
|
||||
|
|
@ -122,6 +124,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
|
|||
SIMD_OP(mean)
|
||||
SIMD_OP(meanSquared)
|
||||
SIMD_OP(clampAll)
|
||||
SIMD_OP(allWithin)
|
||||
}
|
||||
}
|
||||
#undef SIMD_OP
|
||||
|
|
@ -163,6 +166,7 @@ void SIMDDispatch<float>::resetStatus()
|
|||
setStatus(SIMDOps::meanSquared, false);
|
||||
setStatus(SIMDOps::upsampling, true);
|
||||
setStatus(SIMDOps::clampAll, false);
|
||||
setStatus(SIMDOps::allWithin, true);
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -308,7 +312,13 @@ void diff<float>(const float* input, float* output, unsigned size) noexcept
|
|||
template <>
|
||||
void clampAll<float>(float* input, float low, float high, unsigned size) noexcept
|
||||
{
|
||||
return simdDispatch<float>().clampAll(input, low, high, size);
|
||||
simdDispatch<float>().clampAll(input, low, high, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool allWithin<float>(const float* input, float low, float high, unsigned size) noexcept
|
||||
{
|
||||
return simdDispatch<float>().allWithin(input, low, high, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ enum class SIMDOps {
|
|||
meanSquared,
|
||||
upsampling,
|
||||
clampAll,
|
||||
allWithin,
|
||||
_sentinel //
|
||||
};
|
||||
|
||||
|
|
@ -628,7 +629,6 @@ void diff(absl::Span<const T> input, absl::Span<T> output) noexcept
|
|||
*
|
||||
* @tparam T the underlying type
|
||||
* @param input
|
||||
* @param output
|
||||
* @param low
|
||||
* @param high
|
||||
* @param size
|
||||
|
|
@ -648,4 +648,28 @@ void clampAll(absl::Span<T> input, T low, T high) noexcept
|
|||
clampAll<T>(input.data(), low, high, input.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check that all values are within bounds (inclusive)
|
||||
*
|
||||
* @tparam T the underlying type
|
||||
* @param input
|
||||
* @param low
|
||||
* @param high
|
||||
* @param size
|
||||
*/
|
||||
template <class T>
|
||||
bool allWithin(const T* input, T low, T high, unsigned size) noexcept
|
||||
{
|
||||
return allWithinScalar(input, low, high, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool allWithin<float>(const float* input, float low, float high, unsigned size) noexcept;
|
||||
|
||||
template <class T>
|
||||
bool allWithin(absl::Span<const T> input, T low, T high) noexcept
|
||||
{
|
||||
return allWithin<T>(input.data(), low, high, input.size());
|
||||
}
|
||||
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -503,3 +503,44 @@ void clampAllSSE(float* input, float low, float high, unsigned size) noexcept
|
|||
incrementAll(input);
|
||||
}
|
||||
}
|
||||
|
||||
bool allWithinSSE(const float* input, float low, float high, unsigned size) noexcept
|
||||
{
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
if (low > high)
|
||||
std::swap(low, high);
|
||||
|
||||
const auto sentinel = input + size;
|
||||
|
||||
#if SFIZZ_HAVE_SSE2
|
||||
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
|
||||
while (unaligned<ByteAlignment>(input) && input < lastAligned){
|
||||
if (*input < low || *input > high)
|
||||
return false;
|
||||
|
||||
incrementAll(input);
|
||||
}
|
||||
|
||||
const auto mmLow = _mm_set1_ps(low);
|
||||
const auto mmHigh = _mm_set1_ps(high);
|
||||
while (input < lastAligned) {
|
||||
const auto mmIn = _mm_load_ps(input);
|
||||
const auto mmOutside = _mm_or_ps(_mm_cmplt_ps(mmIn, mmLow), _mm_cmpgt_ps(mmIn, mmHigh));
|
||||
if (_mm_movemask_ps(mmOutside) != 0)
|
||||
return false;
|
||||
|
||||
incrementAll<TypeAlignment>(input);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (input < sentinel) {
|
||||
if (*input < low || *input > high)
|
||||
return false;
|
||||
|
||||
incrementAll(input);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ float meanSquaredSSE(const float* vector, unsigned size) noexcept;
|
|||
void cumsumSSE(const float* input, float* output, unsigned size) noexcept;
|
||||
void diffSSE(const float* input, float* output, unsigned size) noexcept;
|
||||
void clampAllSSE(float* input, float low, float high, unsigned size) noexcept;
|
||||
bool allWithinSSE(const float* input, float low, float high, unsigned size) noexcept;
|
||||
|
|
|
|||
|
|
@ -200,3 +200,23 @@ void clampAllScalar(T* input, T low, T high, unsigned size ) noexcept
|
|||
incrementAll(input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool allWithinScalar(const T* input, T low, T high, unsigned size ) noexcept
|
||||
{
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
if (low > high)
|
||||
std::swap(low, high);
|
||||
|
||||
const auto sentinel = input + size;
|
||||
while (input < sentinel) {
|
||||
if (*input < low || *input > high)
|
||||
return false;
|
||||
|
||||
incrementAll(input);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -839,3 +839,14 @@ TEST_CASE("[Helpers] clampAll (SIMD vs scalar)")
|
|||
sfz::clampAll<float>(absl::MakeSpan(inputSIMD), 10.0f, 50.0f);
|
||||
REQUIRE( approxEqual<float>(inputScalar, inputSIMD) );
|
||||
}
|
||||
|
||||
TEST_CASE("[Helpers] allWithin")
|
||||
{
|
||||
std::array<float, 10> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f };
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, false);
|
||||
REQUIRE( sfz::allWithin<float>(input, 0.5f, 11.0f) );
|
||||
REQUIRE( !sfz::allWithin<float>(input, 2.5f, 8.0f) );
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::allWithin, true);
|
||||
REQUIRE( sfz::allWithin<float>(input, 0.5f, 11.0f) );
|
||||
REQUIRE( !sfz::allWithin<float>(input, 2.5f, 8.0f) );
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue