Merge pull request #412 from paulfd/mm-simd-tweaks

MM SIMD tweaks
This commit is contained in:
JP Cimalando 2020-09-09 06:34:10 +02:00 committed by GitHub
commit f53074f223
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 388 additions and 5 deletions

View file

@ -0,0 +1,83 @@
// 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 <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
class MultiplyMul : 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.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
gain = std::vector<float>(state.range(0));
std::fill(output.begin(), output.end(), 2.0f );
std::generate(gain.begin(), gain.end(), [&]() { return dist(gen); });
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<float> gain;
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(MultiplyMul, Straight)(benchmark::State& state) {
for (auto _ : state)
{
for (int i = 0; i < state.range(0); ++i)
output[i] *= gain[i] * input[i];
}
}
BENCHMARK_DEFINE_F(MultiplyMul, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, false);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, false);
sfz::multiplyMul<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(MultiplyMul, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_REGISTER_F(MultiplyMul, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMul, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -0,0 +1,88 @@
// 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 <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
class MultiplyMulFixedGain : 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.1f, 1.0f };
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
gain = dist(gen);
std::fill(output.begin(), output.end(), 2.0f);
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */)
{
}
float gain = {};
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Straight)
(benchmark::State& state)
{
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i)
output[i] *= gain * input[i];
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(MultiplyMulFixedGain, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MultiplyMulFixedGain, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -49,6 +49,8 @@ target_link_libraries(bm_ADSR PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_add BM_add.cpp)
sfizz_add_benchmark(bm_multiplyAdd BM_multiplyAdd.cpp)
sfizz_add_benchmark(bm_multiplyAddFixedGain BM_multiplyAddFixedGain.cpp)
sfizz_add_benchmark(bm_multiplyMul BM_multiplyMul.cpp)
sfizz_add_benchmark(bm_multiplyMulFixedGain BM_multiplyMulFixedGain.cpp)
sfizz_add_benchmark(bm_subtract BM_subtract.cpp)
sfizz_add_benchmark(bm_copy BM_copy.cpp)
sfizz_add_benchmark(bm_mean BM_mean.cpp)

View file

@ -29,6 +29,8 @@ struct SIMDDispatch {
decltype(&divideScalar<T>) divide = &divideScalar<T>;
decltype(&multiplyAddScalar<T>) multiplyAdd = &multiplyAddScalar<T>;
decltype(&multiplyAdd1Scalar<T>) multiplyAdd1 = &multiplyAdd1Scalar<T>;
decltype(&multiplyMulScalar<T>) multiplyMul = &multiplyMulScalar<T>;
decltype(&multiplyMul1Scalar<T>) multiplyMul1 = &multiplyMul1Scalar<T>;
decltype(&linearRampScalar<T>) linearRamp = &linearRampScalar<T>;
decltype(&multiplicativeRampScalar<T>) multiplicativeRamp = &multiplicativeRampScalar<T>;
decltype(&addScalar<T>) add = &addScalar<T>;
@ -81,6 +83,8 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(subtract1)
SIMD_OP(multiplyAdd)
SIMD_OP(multiplyAdd1)
SIMD_OP(multiplyMul)
SIMD_OP(multiplyMul1)
SIMD_OP(copy)
SIMD_OP(cumsum)
SIMD_OP(diff)
@ -118,6 +122,8 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(subtract1)
SIMD_OP(multiplyAdd)
SIMD_OP(multiplyAdd1)
SIMD_OP(multiplyMul)
SIMD_OP(multiplyMul1)
SIMD_OP(copy)
SIMD_OP(cumsum)
SIMD_OP(diff)
@ -158,6 +164,8 @@ void SIMDDispatch<float>::resetStatus()
setStatus(SIMDOps::subtract1, false);
setStatus(SIMDOps::multiplyAdd, false);
setStatus(SIMDOps::multiplyAdd1, false);
setStatus(SIMDOps::multiplyMul, false);
setStatus(SIMDOps::multiplyMul1, false);
setStatus(SIMDOps::copy, false);
setStatus(SIMDOps::cumsum, true);
setStatus(SIMDOps::diff, false);
@ -243,6 +251,18 @@ void multiplyAdd1<float>(float gain, const float* input, float* output, unsigned
return simdDispatch<float>().multiplyAdd1(gain, input, output, size);
}
template <>
void multiplyMul<float>(const float* gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().multiplyMul(gain, input, output, size);
}
template <>
void multiplyMul1<float>(float gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().multiplyMul1(gain, input, output, size);
}
template <>
float linearRamp<float>(float* output, float start, float step, unsigned size) noexcept
{

View file

@ -52,6 +52,8 @@ enum class SIMDOps {
subtract1,
multiplyAdd,
multiplyAdd1,
multiplyMul,
multiplyMul1,
copy,
cumsum,
diff,
@ -322,6 +324,56 @@ void multiplyAdd1(T gain, absl::Span<const T> input, absl::Span<T> output) noexc
multiplyAdd1<T>(gain, input.data(), output.data(), minSpanSize(input, output));
}
/**
* @brief Applies a gain to the input and multiply the output with it
*
* @tparam T the underlying type
* @param gain
* @param input
* @param output
* @param size
*/
template <class T>
void multiplyMul(const T* gain, const T* input, T* output, unsigned size) noexcept
{
multiplyMulScalar(gain, input, output, size);
}
template <>
void multiplyMul<float>(const float* gain, const float* input, float* output, unsigned size) noexcept;
template <class T>
void multiplyMul(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T> output) noexcept
{
CHECK_SPAN_SIZES(gain, input, output);
multiplyMul<T>(gain.data(), input.data(), output.data(), minSpanSize(gain, input, output));
}
/**
* @brief Applies a fixed gain to the input and multiply the output with it
*
* @tparam T the underlying type
* @param gain
* @param input
* @param output
* @param size
*/
template <class T>
void multiplyMul1(T gain, const T* input, T* output, unsigned size) noexcept
{
multiplyMul1Scalar(gain, input, output, size);
}
template <>
void multiplyMul1<float>(float gain, const float* input, float* output, unsigned size) noexcept;
template <class T>
void multiplyMul1(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
{
CHECK_SPAN_SIZES(input, output);
multiplyMul1<T>(gain, input.data(), output.data(), minSpanSize(input, output));
}
/**
* @brief Compute a linear ramp blockwise between 2 values
*

View file

@ -355,16 +355,14 @@ float* ModMatrix::getModulation(TargetId targetId)
}
else {
if (targetFlags & kModIsMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= sourceDepth * sourceBuffer[i];
multiplyMul1<float>(sourceDepth, sourceBuffer, buffer);
}
else if (targetFlags & kModIsPercentMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= (0.01f * sourceDepth) * sourceBuffer[i];
multiplyMul1<float>(0.01f * sourceDepth, sourceBuffer, buffer);
}
else {
ASSERT(targetFlags & kModIsAdditive);
sfz::multiplyAdd1<float>(sourceDepth, sourceBuffer, buffer);
multiplyAdd1<float>(sourceDepth, sourceBuffer, buffer);
}
}
}

View file

@ -183,6 +183,49 @@ void multiplyAdd1SSE(float gain, const float* input, float* output, unsigned siz
*output++ += gain * (*input++);
}
void multiplyMulSSE(const float* gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ *= (*gain++) * (*input++);
while (output < lastAligned) {
auto mmOut = _mm_load_ps(output);
mmOut = _mm_mul_ps(_mm_mul_ps(_mm_load_ps(gain), _mm_load_ps(input)), mmOut);
_mm_store_ps(output, mmOut);
incrementAll<TypeAlignment>(gain, input, output);
}
#endif
while (output < sentinel)
*output++ *= (*gain++) * (*input++);
}
void multiplyMul1SSE(float gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ *= gain * (*input++);
auto mmGain = _mm_set1_ps(gain);
while (output < lastAligned) {
auto mmOut = _mm_load_ps(output);
mmOut = _mm_mul_ps(_mm_mul_ps(mmGain, _mm_load_ps(input)), mmOut);
_mm_store_ps(output, mmOut);
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ *= gain * (*input++);
}
float linearRampSSE(float* output, float start, float step, unsigned size) noexcept
{
const auto sentinel = output + size;

View file

@ -14,6 +14,8 @@ void gain1SSE(float gain, const float* input, float* output, unsigned size) noex
void divideSSE(const float* input, const float* divisor, float* output, unsigned size) noexcept;
void multiplyAddSSE(const float* gain, const float* input, float* output, unsigned size) noexcept;
void multiplyAdd1SSE(float gain, const float* input, float* output, unsigned size) noexcept;
void multiplyMulSSE(const float* gain, const float* input, float* output, unsigned size) noexcept;
void multiplyMul1SSE(float gain, const float* input, float* output, unsigned size) noexcept;
float linearRampSSE(float* output, float start, float step, unsigned size) noexcept;
float multiplicativeRampSSE(float* output, float start, float step, unsigned size) noexcept;
void addSSE(const float* input, float* output, unsigned size) noexcept;

View file

@ -67,6 +67,22 @@ inline void multiplyAdd1Scalar(T gain, const T* input, T* output, unsigned size)
*output++ += gain * (*input++);
}
template <class T>
inline void multiplyMulScalar(const T* gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ *= (*gain++) * (*input++);
}
template <class T>
inline void multiplyMul1Scalar(T gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ *= gain * (*input++);
}
template <class T>
T linearRampScalar(T* output, T start, T step, unsigned size) noexcept
{

View file

@ -517,6 +517,7 @@ TEST_CASE("[Helpers] MultiplyAdd (SIMD)")
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] MultiplyAdd (SIMD vs scalar)")
{
std::vector<float> gain(bigBufferSize);
@ -574,6 +575,84 @@ TEST_CASE("[Helpers] MultiplyAdd fixed gain (SIMD vs scalar)")
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] MultiplyMul (Scalar)")
{
std::array<float, 5> gain { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f };
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 0.0f, 0.8f, 1.8f, 2.4f, 2.0f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
TEST_CASE("[Helpers] MultiplyMul (SIMD)")
{
std::array<float, 5> gain { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f };
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 0.0f, 0.8f, 1.8f, 2.4f, 2.0f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
TEST_CASE("[Helpers] MultiplyMul (SIMD vs Scalar)")
{
std::vector<float> gain(bigBufferSize);
std::vector<float> input(bigBufferSize);
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
absl::c_iota(gain, 0.0f);
absl::c_iota(input, 0.0f);
absl::c_iota(outputScalar, 0.0f);
absl::c_iota(outputSIMD, 0.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, false);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul, true);
sfz::multiplyMul<float>(gain, input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] MultiplyMul fixed gain (Scalar)")
{
float gain = 0.3f;
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 1.5f, 2.4f, 2.7f, 2.4f, 1.5f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] MultiplyMul fixed gain (SIMD)")
{
float gain = 0.3f;
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 1.5f, 2.4f, 2.7f, 2.4f, 1.5f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] MultiplyMul fixed gain (SIMD vs scalar)")
{
float gain = 0.3f;
std::vector<float> input(bigBufferSize);
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
absl::c_iota(input, 0.0f);
absl::c_iota(outputScalar, 0.0f);
absl::c_iota(outputSIMD, 0.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, false);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyMul1, true);
sfz::multiplyMul1<float>(gain, input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] Subtract")
{
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };