diff --git a/benchmarks/BM_maps.cpp b/benchmarks/BM_maps.cpp new file mode 100644 index 00000000..88edd380 --- /dev/null +++ b/benchmarks/BM_maps.cpp @@ -0,0 +1,350 @@ +// 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 +#include +#include +#include +#include "../src/sfizz/Range.h" +#include +#include + +constexpr int maxCC { 256 }; + +class MyFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) + { + std::random_device rd {}; + std::mt19937 gen { rd() }; + std::uniform_real_distribution distFloat { 0.1f, 1.0f }; + std::uniform_int_distribution distInt { 1, maxCC }; + floats = std::vector(state.range(0)); + ccs = std::vector(state.range(0)); + ranges = std::vector>(state.range(0)); + absl::c_generate(floats, [&]() { + return distFloat(gen); + }); + absl::c_generate(ccs, [&]() { + return distInt(gen); + }); + absl::c_generate(ranges, [&]() { + return sfz::Range(distInt(gen), distInt(gen)); + }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) + { + } + + std::vector ccs; + std::vector> ranges; + std::vector floats; +}; + +template +struct CCValuePair { + int cc; + ValueType value; +}; + +template +struct CCValuePairComparator { + bool operator()(const CCValuePair& valuePair, const int& cc) + { + return (valuePair.cc < cc); + } + + bool operator()(const int& cc, const CCValuePair& valuePair) + { + return (cc < valuePair.cc); + } + + bool operator()(const CCValuePair& lhs, const CCValuePair& rhs) + { + return (lhs.cc < rhs.cc); + } +}; + +template +struct CCValuePairComparator { + bool operator()(const CCValuePair& valuePair, const ValueType& value) + { + return (valuePair.value < value); + } + + bool operator()(const ValueType& value, const CCValuePair& valuePair) + { + return (value < valuePair.value); + } + + bool operator()(const CCValuePair& lhs, const CCValuePair& rhs) + { + return (lhs.value < rhs.value); + } +}; + +template +class CCMap { +public: + CCMap() = delete; + /** + * @brief Construct a new CCMap object with the specified default value. + * + * @param defaultValue + */ + CCMap(const ValueType& defaultValue) + : defaultValue(defaultValue) + { + } + CCMap(CCMap&&) = default; + CCMap(const CCMap&) = default; + ~CCMap() = default; + + /** + * @brief Returns the held object at the index, or a default value if not present + * + * @param index + * @return const ValueType& + */ + const ValueType& getWithDefault(int index) const noexcept + { + auto it = absl::c_lower_bound(container, index, CCValuePairComparator{}); + if (it == container.end() || it->cc != index) { + return defaultValue; + } else { + return it->value; + } + } + + /** + * @brief Get the value at index or emplace a new one if not present + * + * @param index the index of the element + * @return ValueType& + */ + ValueType& operator[](const int& index) noexcept + { + auto it = absl::c_lower_bound(container, index, CCValuePairComparator{}); + if (it == container.end() || it->cc != index) { + auto inserted = container.insert(it, { index, defaultValue }); + return inserted->value; + } else { + return it->value; + } + } + + /** + * @brief Is the container empty + * + * @return true + * @return false + */ + inline bool empty() const { return container.empty(); } + /** + * @brief Returns true if the container containers an element at index + * + * @param index + * @return true + * @return false + */ + bool contains(int index) const noexcept + { + return absl::c_binary_search(container, index, CCValuePairComparator{}); + } + typename std::vector>::const_iterator begin() const { return container.cbegin(); } + typename std::vector>::const_iterator end() const { return container.cend(); } +private: + // typename std::vector>::iterator begin() { return container.begin(); } + // typename std::vector>::iterator end() { return container.end(); } + + const ValueType defaultValue; + std::vector> container; +}; + +BENCHMARK_DEFINE_F(MyFixture, FillVector_Float) +(benchmark::State& state) +{ + for (auto _ : state) { + CCMap map { 0 }; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, FillVector_Range) +(benchmark::State& state) +{ + for (auto _ : state) { + CCMap> map { sfz::Range(0, 127) }; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = ranges[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, FillAbseilFlatHM_Float) +(benchmark::State& state) +{ + for (auto _ : state) { + absl::flat_hash_map map; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, FillAbseilFlatHM_Range) +(benchmark::State& state) +{ + for (auto _ : state) { + absl::flat_hash_map> map; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = ranges[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupBaseline_Float) +(benchmark::State& state) +{ + std::vector output; + output.resize(state.range(0)); + + std::vector map; + output.reserve(state.range(0)); + for (int i = 0; i < state.range(0); ++i) + map.push_back(floats[i]); + + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupBaseline_Range) +(benchmark::State& state) +{ + std::vector> output; + output.resize(state.range(0)); + + std::vector> map; + output.reserve(state.range(0)); + for (int i = 0; i < state.range(0); ++i) + map.push_back(ranges[i]); + + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[i]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupVector_Float) +(benchmark::State& state) +{ + std::vector output; + output.resize(state.range(0)); + + CCMap map { 0 }; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[ccs[i]]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupVector_Range) +(benchmark::State& state) +{ + std::vector> output; + output.resize(state.range(0)); + + CCMap> map { sfz::Range(0, 127) }; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = ranges[i]; + + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[ccs[i]]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupAbseilFlatHM_Float) +(benchmark::State& state) +{ + std::vector output; + output.resize(state.range(0)); + + absl::flat_hash_map map; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[ccs[i]]; + } +} + +BENCHMARK_DEFINE_F(MyFixture, LookupAbseilFlatHM_Range) +(benchmark::State& state) +{ + std::vector> output; + output.resize(state.range(0)); + + absl::flat_hash_map> map; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = ranges[i]; + + for (auto _ : state) { + for (int i = 0; i < state.range(0); ++i) + output[i] = map[ccs[i]]; + } +} + + +BENCHMARK_DEFINE_F(MyFixture, IterateVector_Float) +(benchmark::State& state) +{ + std::vector output; + output.reserve(maxCC); + + CCMap map { 0 }; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + + for (auto _ : state) { + for (auto& pair: map) + output.push_back(pair.value); + } +} + +BENCHMARK_DEFINE_F(MyFixture, IterateAbseilFlatHM_Float) +(benchmark::State& state) +{ + std::vector output; + output.reserve(maxCC); + + absl::flat_hash_map map; + for (int i = 0; i < state.range(0); ++i) + map[ccs[i]] = floats[i]; + for (auto _ : state) { + for (auto& pair: map) + output.push_back(pair.second); + } +} + + +BENCHMARK_REGISTER_F(MyFixture, FillVector_Float)->RangeMultiplier(2)->Range(16, 512); +// BENCHMARK_REGISTER_F(MyFixture, FillVector_Range)->RangeMultiplier(2)->Range(16, 512); +BENCHMARK_REGISTER_F(MyFixture, FillAbseilFlatHM_Float)->RangeMultiplier(2)->Range(16, 512); +// BENCHMARK_REGISTER_F(MyFixture, FillAbseilFlatHM_Range)->RangeMultiplier(2)->Range(16, 512); +BENCHMARK_REGISTER_F(MyFixture, LookupBaseline_Float)->RangeMultiplier(2)->Range(16, 512); +// BENCHMARK_REGISTER_F(MyFixture, LookupBaseline_Range)->RangeMultiplier(2)->Range(16, 512); +BENCHMARK_REGISTER_F(MyFixture, LookupVector_Float)->RangeMultiplier(2)->Range(16, 512); +// BENCHMARK_REGISTER_F(MyFixture, LookupVector_Range)->RangeMultiplier(2)->Range(16, 512); +BENCHMARK_REGISTER_F(MyFixture, LookupAbseilFlatHM_Float)->RangeMultiplier(2)->Range(16, 512); +// BENCHMARK_REGISTER_F(MyFixture, LookupAbseilFlatHM_Range)->RangeMultiplier(2)->Range(16, 512); +BENCHMARK_REGISTER_F(MyFixture, IterateVector_Float)->Range(maxCC, maxCC); +BENCHMARK_REGISTER_F(MyFixture, IterateAbseilFlatHM_Float)->Range(maxCC, maxCC); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index b143ac5a..8931eca6 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -58,6 +58,8 @@ sfizz_add_benchmark(bm_diff BM_diff.cpp) sfizz_add_benchmark(bm_widthPos BM_widthPos.cpp) sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp) sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp) +sfizz_add_benchmark(bm_maps BM_maps.cpp) +target_link_libraries(bm_maps PRIVATE absl::flat_hash_map) sfizz_add_benchmark(bm_logger BM_logger.cpp) target_link_libraries(bm_logger PRIVATE sfizz::sfizz)