/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstddef>
#include <random>
#include <vector>
#include <benchmark/benchmark.h>
#include <audio_utils/Statistics.h>
template <typename T>
static void initUniform(std::vector<T> &data, T rangeMin, T rangeMax) {
const size_t count = data.capacity();
std::minstd_rand gen(count);
std::uniform_real_distribution<T> dis(rangeMin, rangeMax);
for (auto &datum : data) {
datum = dis(gen);
}
}
template <typename Stats>
static void BM_MeanVariance(benchmark::State& state, int iterlimit, int alphalimit) {
const float alpha = 1. - alphalimit * std::numeric_limits<float>::epsilon();
Stats stat(alpha);
using T = decltype(stat.getMin());
constexpr size_t count = 1 << 20; // exactly one "mega" samples from the distribution.
constexpr T range = 1.;
std::vector<T> data(count);
initUniform(data, -range, range);
// Run the test
int iters = 0;
while (state.KeepRunning()) {
benchmark::DoNotOptimize(data.data());
for (const auto &datum : data) {
stat.add(datum);
}
benchmark::ClobberMemory();
if (++iters % iterlimit == 0) {
printf("%d> alpha:%f mean:%.17g variance:%.17g\n",
iters, alpha, (double)stat.getMean(), (double)stat.getPopVariance());
stat.reset();
}
}
state.SetComplexityN(count);
}
// Test case:
// Do we work correctly within the capacity of float statistics when alpha == 1?
//
// 1 << 23 samples is the mantissa limited capacity of float statistics if alpha == 1.
static constexpr int float_iterlimit = 8;
// alphalimit of 0 means alpha exactly equals one.
static constexpr int alpha_equals_one_alphalimit = 0;
// benchmark running float
static void BM_MeanVariance_float_float_float(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, float, float>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_MeanVariance_float_float_float);
// benchmark reference float
static void BM_RefMeanVariance_float_float(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::ReferenceStatistics<float, float>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_RefMeanVariance_float_float);
// benchmark running double
static auto BM_MeanVariance_float_double_double(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, double, double>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_MeanVariance_float_double_double);
// benchmark reference double
static auto BM_RefMeanVariance_float_double(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::ReferenceStatistics<float, double>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_RefMeanVariance_float_double);
// benchmark running float + kahan
static auto BM_MeanVariance_float_float_Kahan(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, float,
android::audio_utils::KahanSum<float>>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_MeanVariance_float_float_Kahan);
// benchmark running float + Neumaier
static auto BM_MeanVariance_float_float_Neumaier(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, float,
android::audio_utils::NeumaierSum<float>>>(state,
float_iterlimit, alpha_equals_one_alphalimit);
}
BENCHMARK(BM_MeanVariance_float_float_Neumaier);
// Test case:
// Do we work correctly for very large N statistics when alpha is 1 - 32 * epsilon?
// This simulates long term statistics collection, where the alpha weighted windowing
// permits us to exceed 1 << 23 samples reliably.
//
// 1 << 25 samples exceeds the mantissa limited capacity of float statistics if alpha == 1...
static constexpr int float_overflow_iterlimit = 32;
// but we use an alphalimit of 32, means 1. - (alphalimit * epsilon) approx = 0.999996.
// This should allow statistics collection indefinitely.
static constexpr int alpha_safe_upperbound_iterlimit = 32;
// benchmark running float at alpha
static auto BM_MeanVariance_float_float_float_alpha(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, float, float>>(state,
float_overflow_iterlimit, alpha_safe_upperbound_iterlimit);
}
BENCHMARK(BM_MeanVariance_float_float_float_alpha);
// benchmark running double
static auto BM_MeanVariance_float_double_double_alpha(benchmark::State &state) {
BM_MeanVariance<android::audio_utils::Statistics<float, double, double>>(state,
float_overflow_iterlimit, alpha_safe_upperbound_iterlimit);
}
BENCHMARK(BM_MeanVariance_float_double_double_alpha);
BENCHMARK_MAIN();