Add an allWithin SIMD helper

This commit is contained in:
Paul Ferrand 2020-08-12 16:54:49 +02:00
parent ff8ac64d61
commit c034fd3e30
8 changed files with 180 additions and 2 deletions

View 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();

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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) );
}