From 89a78d0b4d56989a4dc15a3fb842d3baba4a75c3 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 17 Feb 2025 15:26:31 -0500 Subject: [PATCH] performance testing and lfo improvements (#178) * performance testing and lfo improvements * Fix a header * more missing headers in perftest --- CMakeLists.txt | 13 +++ cmake/CPM.cmake | 28 ++---- .../sst/basic-blocks/modulators/SimpleLFO.h | 14 ++- tests/perf/lfo.cpp | 96 +++++++++++++++++++ tests/perf/perf_test.cpp | 31 ++++++ tests/perf/perfutils.h | 55 +++++++++++ 6 files changed, 216 insertions(+), 21 deletions(-) create mode 100644 tests/perf/lfo.cpp create mode 100644 tests/perf/perf_test.cpp create mode 100644 tests/perf/perfutils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a8a0b60..4c3cdaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,13 @@ if (${SST_BASIC_BLOCKS_BUILD_TESTS}) tests/modulator_tests.cpp tests/mod_matrix_tests.cpp ) + + + add_executable(sst-basic-blocks-perf-test + tests/perf/perf_test.cpp + tests/perf/lfo.cpp + ) + if (NOT TARGET simde) message(STATUS "Importing SIMDE with CPM") CPMAddPackage(NAME simde @@ -47,11 +54,17 @@ if (${SST_BASIC_BLOCKS_BUILD_TESTS}) target_include_directories(sst-basic-blocks-test PRIVATE ${simde_SOURCE_DIR} tests) + target_include_directories(sst-basic-blocks-perf-test PRIVATE + ${simde_SOURCE_DIR} + tests/perf) else () target_link_libraries(sst-basic-blocks-test PRIVATE simde) + target_link_libraries(sst-basic-blocks-perf-test PRIVATE simde) endif () target_link_libraries(sst-basic-blocks-test PRIVATE fmt ${PROJECT_NAME}) + target_link_libraries(sst-basic-blocks-perf-test PRIVATE fmt ${PROJECT_NAME}) + target_include_directories(sst-basic-blocks-test PRIVATE libs/catch2) if ((DEFINED ${CMAKE_OSX_DEPLOYMENT_TARGET}) AND ("${CMAKE_OSX_DEPLOYMENT_TARGET}" VERSION_LESS "10.12")) diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake index eaa397b..47e2e87 100644 --- a/cmake/CPM.cmake +++ b/cmake/CPM.cmake @@ -1,4 +1,9 @@ -set(CPM_DOWNLOAD_VERSION 0.36.0) +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.40.5) +set(CPM_HASH_SUM "c46b876ae3b9f994b4f05a4c15553e0485636862064f1fcc9d8b4f832086bc5d") if(CPM_SOURCE_CACHE) set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") @@ -11,22 +16,9 @@ endif() # Expand relative path. This is important if the provided path contains a tilde (~) get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) -function(download_cpm) - message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") - file(DOWNLOAD - https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake - ${CPM_DOWNLOAD_LOCATION} - ) -endfunction() - -if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) - download_cpm() -else() - # resume download if it previously failed - file(READ ${CPM_DOWNLOAD_LOCATION} check) - if("${check}" STREQUAL "") - download_cpm() - endif() -endif() +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) include(${CPM_DOWNLOAD_LOCATION}) diff --git a/include/sst/basic-blocks/modulators/SimpleLFO.h b/include/sst/basic-blocks/modulators/SimpleLFO.h index e71ae84..21186f1 100644 --- a/include/sst/basic-blocks/modulators/SimpleLFO.h +++ b/include/sst/basic-blocks/modulators/SimpleLFO.h @@ -39,7 +39,7 @@ namespace sst::basic_blocks::modulators { // For context on SRProvider see the ADSRDAHD Envelope -template struct SimpleLFO +template struct SimpleLFO { SRProvider *srProvider{nullptr}; @@ -127,8 +127,10 @@ template struct SimpleLFO { if (d == 0) return x; + if constexpr (clampDeform) + d = std::clamp(d, -3.f, 3.f); + auto a = 0.5 * d; - auto a = 0.5 * std::clamp(d, -3.f, 3.f); x = x - a * x * x + a; x = x - a * x * x + a; return x; @@ -193,12 +195,18 @@ template struct SimpleLFO } } + float lastRate{-123485924.0}, lastFRate{0}; inline void process_block(const float r, const float d, const int lshape, bool reverse = false, float tsScale = 1.f) { float target{0.f}; - auto frate = tsScale * srProvider->envelope_rate_linear_nowrap(-r); + auto frate = lastFRate; + if (r != lastRate) + { + frate = tsScale * srProvider->envelope_rate_linear_nowrap(-r); + lastRate = r; + } phase += frate * (reverse ? -1 : 1); int phaseMidpoint{0}; bool phaseTurned{false}; diff --git a/tests/perf/lfo.cpp b/tests/perf/lfo.cpp new file mode 100644 index 0000000..946a1e3 --- /dev/null +++ b/tests/perf/lfo.cpp @@ -0,0 +1,96 @@ +/* + * sst-basic-blocks - an open source library of core audio utilities + * built by Surge Synth Team. + * + * Provides a collection of tools useful on the audio thread for blocks, + * modulation, etc... or useful for adapting code to multiple environments. + * + * Copyright 2023, various authors, as described in the GitHub + * transaction log. Parts of this code are derived from similar + * functions original in Surge or ShortCircuit. + * + * sst-basic-blocks is released under the GNU General Public Licence v3 + * or later (GPL-3.0-or-later). The license is found in the "LICENSE" + * file in the root of this repository, or at + * https://www.gnu.org/licenses/gpl-3.0.en.html. + * + * A very small number of explicitly chosen header files can also be + * used in an MIT/BSD context. Please see the README.md file in this + * repo or the comments in the individual files. Only headers with an + * explicit mention that they are dual licensed may be copied and reused + * outside the GPL3 terms. + * + * All source in sst-basic-blocks available at + * https://github.com/surge-synthesizer/sst-basic-blocks + */ + +#include +#include + +#include "sst/basic-blocks/modulators/SimpleLFO.h" +#include "sst/basic-blocks/tables/TwoToTheXProvider.h" +#include "sst/basic-blocks/dsp/RNG.h" +#include "perfutils.h" + +template struct SRProvider +{ + const sst::basic_blocks::tables::TwoToTheXProvider &ttx; + SRProvider(const sst::basic_blocks::tables::TwoToTheXProvider &t) : ttx(t) {} + float envelope_rate_linear_nowrap(float f) const + { + return (blockSize * sampleRateInv) * ttx.twoToThe(-f); + } + + void setSampleRate(double sr) + { + samplerate = sr; + sampleRate = sr; + sampleRateInv = 1.0 / sr; + } + double samplerate{1}; + double sampleRate{1}; + double sampleRateInv{1}; +}; + +template +void basicTest(sst::basic_blocks::tables::TwoToTheXProvider &ttx, sst::basic_blocks::dsp::RNG &rng) +{ + using srp_t = SRProvider; + using lfo_t = sst::basic_blocks::modulators::SimpleLFO, blockSize>; + + auto srp = srp_t(ttx); + srp.setSampleRate(48000 * 2.5); + + std::array lfos{ + lfo_t(&srp, rng), lfo_t(&srp, rng), lfo_t(&srp, rng), lfo_t(&srp, rng), + lfo_t(&srp, rng), lfo_t(&srp, rng), lfo_t(&srp, rng), lfo_t(&srp, rng), + }; + + for (auto sh = lfo_t::SINE; sh <= lfo_t::SH_NOISE; sh = (typename lfo_t::Shape)((int)sh + 1)) + for (auto def : {-0.1f, 0.f, 0.2f}) + { + { + auto blocks = (int)(200 * srp.sampleRate / blockSize); + perf::TimeGuard tg("LFO - shape=" + std::to_string(sh) + + " def=" + std::to_string(def), + __FILE__, __LINE__, 248688); + for (int lf = 0; lf < lfos.size(); ++lf) + lfos[lf].attack(sh); + + for (int i = 0; i < blocks; ++i) + { + for (int lf = 0; lf < lfos.size(); ++lf) + lfos[lf].process_block(2.4, def, sh, false, 1.0); + } + } + } +} + +void lfoPerformance() +{ + sst::basic_blocks::tables::TwoToTheXProvider ttxlfo; + sst::basic_blocks::dsp::RNG rng(782567); + ttxlfo.init(); + std::cout << __FILE__ << ":" << __LINE__ << " LFO Perf starting" << std::endl; + basicTest<8>(ttxlfo, rng); +} \ No newline at end of file diff --git a/tests/perf/perf_test.cpp b/tests/perf/perf_test.cpp new file mode 100644 index 0000000..90e7ab0 --- /dev/null +++ b/tests/perf/perf_test.cpp @@ -0,0 +1,31 @@ +/* + * sst-basic-blocks - an open source library of core audio utilities + * built by Surge Synth Team. + * + * Provides a collection of tools useful on the audio thread for blocks, + * modulation, etc... or useful for adapting code to multiple environments. + * + * Copyright 2023, various authors, as described in the GitHub + * transaction log. Parts of this code are derived from similar + * functions original in Surge or ShortCircuit. + * + * sst-basic-blocks is released under the GNU General Public Licence v3 + * or later (GPL-3.0-or-later). The license is found in the "LICENSE" + * file in the root of this repository, or at + * https://www.gnu.org/licenses/gpl-3.0.en.html. + * + * A very small number of explicitly chosen header files can also be + * used in an MIT/BSD context. Please see the README.md file in this + * repo or the comments in the individual files. Only headers with an + * explicit mention that they are dual licensed may be copied and reused + * outside the GPL3 terms. + * + * All source in sst-basic-blocks available at + * https://github.com/surge-synthesizer/sst-basic-blocks + */ + +#include + +extern void lfoPerformance(); + +int main(int argc, char **argv) { lfoPerformance(); } diff --git a/tests/perf/perfutils.h b/tests/perf/perfutils.h new file mode 100644 index 0000000..3d8cd4e --- /dev/null +++ b/tests/perf/perfutils.h @@ -0,0 +1,55 @@ +/* + * sst-basic-blocks - an open source library of core audio utilities + * built by Surge Synth Team. + * + * Provides a collection of tools useful on the audio thread for blocks, + * modulation, etc... or useful for adapting code to multiple environments. + * + * Copyright 2023, various authors, as described in the GitHub + * transaction log. Parts of this code are derived from similar + * functions original in Surge or ShortCircuit. + * + * sst-basic-blocks is released under the GNU General Public Licence v3 + * or later (GPL-3.0-or-later). The license is found in the "LICENSE" + * file in the root of this repository, or at + * https://www.gnu.org/licenses/gpl-3.0.en.html. + * + * A very small number of explicitly chosen header files can also be + * used in an MIT/BSD context. Please see the README.md file in this + * repo or the comments in the individual files. Only headers with an + * explicit mention that they are dual licensed may be copied and reused + * outside the GPL3 terms. + * + * All source in sst-basic-blocks available at + * https://github.com/surge-synthesizer/sst-basic-blocks + */ + +#ifndef SST_BASIC_BLOCK_TESTS_PERF_PERFUTILS_H +#define SST_BASIC_BLOCK_TESTS_PERF_PERFUTILS_H + +#include +#include +#include + +namespace perf +{ +struct TimeGuard +{ + std::string m; + int d; + std::chrono::high_resolution_clock::time_point t; + TimeGuard(const std::string &msg, const std::string &f, int l, int divisor = 1) : d(divisor) + { + m = f + ":" + std::to_string(l) + " " + msg; + t = std::chrono::high_resolution_clock::now(); + } + ~TimeGuard() + { + auto e = std::chrono::high_resolution_clock::now(); + auto us = std::chrono::duration_cast(e - t).count(); + std::cout << m << " " << us << " us; pct=" << 100.0 * us / d << "%" << std::endl; + } +}; +}; // namespace perf + +#endif \ No newline at end of file