Add clamping SIMD helper

This commit is contained in:
Paul Ferrand 2020-08-12 16:12:04 +02:00
parent f94e868155
commit ff8ac64d61
8 changed files with 162 additions and 0 deletions

52
benchmarks/BM_clamp.cpp Normal file
View file

@ -0,0 +1,52 @@
// 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 ClampArray : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, 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(ClampArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, false);
sfz::clampAll<float>(absl::MakeSpan(input), 1.2f, 3.8f);
}
}
BENCHMARK_DEFINE_F(ClampArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, true);
sfz::clampAll<float>(absl::MakeSpan(input), 1.2f, 3.8f);
}
}
BENCHMARK_REGISTER_F(ClampArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(ClampArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -59,6 +59,7 @@ sfizz_add_benchmark(bm_maps BM_maps.cpp)
target_link_libraries(bm_maps PRIVATE absl::flat_hash_map) target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp) sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp)
sfizz_add_benchmark(bm_random BM_random.cpp) sfizz_add_benchmark(bm_random BM_random.cpp)
sfizz_add_benchmark(bm_clamp BM_clamp.cpp)
sfizz_add_benchmark(bm_logger BM_logger.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp)
target_link_libraries(bm_logger PRIVATE sfizz::sfizz) target_link_libraries(bm_logger PRIVATE sfizz::sfizz)

View file

@ -40,6 +40,7 @@ struct SIMDDispatch {
decltype(&diffScalar<T>) diff = &diffScalar<T>; decltype(&diffScalar<T>) diff = &diffScalar<T>;
decltype(&meanScalar<T>) mean = &meanScalar<T>; decltype(&meanScalar<T>) mean = &meanScalar<T>;
decltype(&meanSquaredScalar<T>) meanSquared = &meanSquaredScalar<T>; decltype(&meanSquaredScalar<T>) meanSquared = &meanSquaredScalar<T>;
decltype(&clampAllScalar<T>) clampAll = &clampAllScalar<T>;
private: private:
std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus; std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
@ -84,6 +85,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(diff) SIMD_OP(diff)
SIMD_OP(mean) SIMD_OP(mean)
SIMD_OP(meanSquared) SIMD_OP(meanSquared)
SIMD_OP(clampAll)
} }
#undef SIMD_OP #undef SIMD_OP
} }
@ -119,6 +121,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(diff) SIMD_OP(diff)
SIMD_OP(mean) SIMD_OP(mean)
SIMD_OP(meanSquared) SIMD_OP(meanSquared)
SIMD_OP(clampAll)
} }
} }
#undef SIMD_OP #undef SIMD_OP
@ -159,6 +162,7 @@ void SIMDDispatch<float>::resetStatus()
setStatus(SIMDOps::mean, false); setStatus(SIMDOps::mean, false);
setStatus(SIMDOps::meanSquared, false); setStatus(SIMDOps::meanSquared, false);
setStatus(SIMDOps::upsampling, true); setStatus(SIMDOps::upsampling, true);
setStatus(SIMDOps::clampAll, false);
} }
/// ///
@ -301,4 +305,10 @@ void diff<float>(const float* input, float* output, unsigned size) noexcept
return simdDispatch<float>().diff(input, output, size); return simdDispatch<float>().diff(input, output, size);
} }
template <>
void clampAll<float>(float* input, float low, float high, unsigned size) noexcept
{
return simdDispatch<float>().clampAll(input, low, high, size);
}
} }

View file

@ -59,6 +59,7 @@ enum class SIMDOps {
mean, mean,
meanSquared, meanSquared,
upsampling, upsampling,
clampAll,
_sentinel // _sentinel //
}; };
@ -622,4 +623,29 @@ void diff(absl::Span<const T> input, absl::Span<T> output) noexcept
diff<T>(input.data(), output.data(), minSpanSize(input, output)); diff<T>(input.data(), output.data(), minSpanSize(input, output));
} }
/**
* @brief Clamp a vector between a low and high bound
*
* @tparam T the underlying type
* @param input
* @param output
* @param low
* @param high
* @param size
*/
template <class T>
void clampAll(T* input, T low, T high, unsigned size) noexcept
{
clampAllScalar(input, low, high, size);
}
template <>
void clampAll<float>(float* input, float low, float high, unsigned size) noexcept;
template <class T>
void clampAll(absl::Span<T> input, T low, T high) noexcept
{
clampAll<T>(input.data(), low, high, input.size());
}
} // namespace sfz } // namespace sfz

View file

@ -472,3 +472,34 @@ void diffSSE(const float* input, float* output, unsigned size) noexcept
incrementAll(input, output); incrementAll(input, output);
} }
} }
void clampAllSSE(float* input, float low, float high, unsigned size) noexcept
{
if (size == 0)
return;
const auto sentinel = input + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input) && input < lastAligned){
const float clampedAbove = *input > high ? high : *input;
*input = clampedAbove < low ? low : clampedAbove;
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);
_mm_store_ps(input, _mm_max_ps(_mm_min_ps(mmIn, mmHigh), mmLow));
incrementAll<TypeAlignment>(input);
}
#endif
while (input < sentinel) {
const float clampedAbove = *input > high ? high : *input;
*input = clampedAbove < low ? low : clampedAbove;
incrementAll(input);
}
}

View file

@ -25,3 +25,4 @@ float meanSSE(const float* vector, unsigned size) noexcept;
float meanSquaredSSE(const float* vector, unsigned size) noexcept; float meanSquaredSSE(const float* vector, unsigned size) noexcept;
void cumsumSSE(const float* input, float* output, unsigned size) noexcept; void cumsumSSE(const float* input, float* output, unsigned size) noexcept;
void diffSSE(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;

View file

@ -186,3 +186,17 @@ void diffScalar(const T* input, T* output, unsigned size) noexcept
incrementAll(input, output); incrementAll(input, output);
} }
} }
template <class T>
void clampAllScalar(T* input, T low, T high, unsigned size ) noexcept
{
if (size == 0)
return;
const auto sentinel = input + size;
while (input < sentinel) {
const float clampedAbove = *input > high ? high : *input;
*input = clampedAbove < low ? low : clampedAbove;
incrementAll(input);
}
}

View file

@ -812,3 +812,30 @@ TEST_CASE("[Helpers] Width Scalar")
REQUIRE(right[0] == Approx(1.0f).margin(0.001f)); REQUIRE(right[0] == Approx(1.0f).margin(0.001f));
} }
} }
TEST_CASE("[Helpers] clampAll")
{
std::array<float, 10> inputScalar { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f };
std::array<float, 10> inputSIMD;
sfz::copy<float>(inputScalar, absl::MakeSpan(inputSIMD));
std::array<float, 10> expected { 2.5f, 2.5f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, false);
sfz::clampAll<float>(absl::MakeSpan(inputScalar), 2.5f, 8.0f);
REQUIRE( approxEqual<float>(inputScalar, expected) );
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, true);
sfz::clampAll<float>(absl::MakeSpan(inputSIMD), 2.5f, 8.0f);
REQUIRE( approxEqual<float>(inputSIMD, expected) );
}
TEST_CASE("[Helpers] clampAll (SIMD vs scalar)")
{
std::vector<float> inputScalar(medBufferSize);
std::vector<float> inputSIMD(medBufferSize);
absl::c_iota(inputScalar, 2.0f);
sfz::copy<float>(inputScalar, absl::MakeSpan(inputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, false);
sfz::clampAll<float>(absl::MakeSpan(inputScalar), 10.0f, 50.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::clampAll, true);
sfz::clampAll<float>(absl::MakeSpan(inputSIMD), 10.0f, 50.0f);
REQUIRE( approxEqual<float>(inputScalar, inputSIMD) );
}