Added a mean (reduce) SIMD/scalar helper

This commit is contained in:
paulfd 2019-09-13 21:17:52 +02:00
parent ebd2b1906a
commit c4f681c371
7 changed files with 165 additions and 1 deletions

90
benchmarks/BM_mean.cpp Normal file
View file

@ -0,0 +1,90 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../sfizz/SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <cmath>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>
class MeanArray : 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, 1 };
input = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& state [[maybe_unused]])
{
}
std::vector<float> input;
};
BENCHMARK_DEFINE_F(MeanArray, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
auto result = mean<float, false>(input);
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MeanArray, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
auto result = mean<float, true>(input);
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MeanArray, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
auto result = mean<float, false>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MeanArray, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
auto result = mean<float, true>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_REGISTER_F(MeanArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(MeanArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -69,11 +69,15 @@ target_link_libraries(bm_copy benchmark absl::span absl::algorithm)
add_executable(bm_pan BM_pan.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_pan benchmark absl::span absl::algorithm)
add_executable(bm_mean BM_mean.cpp ${SFIZZ_SIMD_SOURCES})
target_link_libraries(bm_mean benchmark absl::span absl::algorithm)
add_custom_target(sfizz_benchmarks)
add_dependencies(sfizz_benchmarks
bm_opf_high_vs_low
bm_write
bm_read
bm_mean
bm_fill
bm_mathfuns
bm_gain

View file

@ -59,4 +59,5 @@ namespace SIMDConfig {
constexpr bool multiplyAdd { false };
constexpr bool copy { false };
constexpr bool pan { true };
constexpr bool mean { true };
}

View file

@ -130,4 +130,10 @@ template <>
void pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
pan<float, false>(panEnvelope, leftBuffer, rightBuffer);
}
template <>
float mean<float, true>(absl::Span<const float> vector) noexcept
{
return mean<float, false>(vector);
}

View file

@ -412,4 +412,21 @@ void pan(absl::Span<const T> panEnvelope, absl::Span<T> leftBuffer, absl::Span<T
}
template <>
void pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept;
void pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept;
template <class T, bool SIMD = SIMDConfig::mean>
T mean(absl::Span<const T> vector) noexcept
{
T result { 0.0 };
if (vector.size() == 0)
return result;
auto* value = vector.begin();
while (value < vector.end())
result += *value++;
return result / static_cast<T>(vector.size());
}
template <>
float mean<float, true>(absl::Span<const float> vector) noexcept;

View file

@ -600,4 +600,36 @@ void pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> lef
while (pan < sentinel)
snippetPan(pan, left, right);
}
template <>
float mean<float, true>(absl::Span<const float> vector) noexcept
{
float result { 0.0 };
if (vector.size() == 0)
return result;
auto* value = vector.begin();
auto* sentinel = vector.end();
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(value))
result += *value++;
auto mmValues = _mm_setzero_ps();
while(value < lastAligned) {
mmValues = _mm_add_ps(mmValues, _mm_load_ps(value));
value += TypeAlignment;
}
std::array<float, 4> sseResult;
_mm_store_ps(sseResult.data(), mmValues);
for (auto sseValue: sseResult)
result += sseValue;
while (value < sentinel)
result += *value++;
return result / static_cast<float>(vector.size());
}

View file

@ -684,3 +684,17 @@ TEST_CASE("[Helpers] copy (SIMD vs scalar)")
add<float, true>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] Mean")
{
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 };
REQUIRE(mean<float, false>(input) == 5.5f);
REQUIRE(mean<float, true>(input) == 5.5f);
}
TEST_CASE("[Helpers] Mean (SIMD vs scalar)")
{
std::vector<float> input(bigBufferSize);
absl::c_iota(input, 0.0);
REQUIRE(mean<float, false>(input) == mean<float, true>(input));
}