Add clamping SIMD helper
This commit is contained in:
parent
f94e868155
commit
ff8ac64d61
8 changed files with 162 additions and 0 deletions
52
benchmarks/BM_clamp.cpp
Normal file
52
benchmarks/BM_clamp.cpp
Normal 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();
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) );
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue