#pragma once #include #define __STDC_FORMAT_MACROS #include #include "TimeStatsCollector.h" template class TestBuffers; /** * class MeasureTime. * * Used to estimate the amount of time a function takes to execute. * Will run the function over and over in a tight loop. Return value of function * is saved to testBuffers. Otherwise the compiler might optimize the whole thing away. * Usually ends by printing out the data. * * One of that statistics printed out is "quota used per 1 percent". Here we are arbitrarily * stating that a synthesizer module "should" use only one percent of the available audio processing CPU. * This won't be the case for all plugins/developers, but certainly is a plausible benchmark. * * A big caveat to all this is that code running inside VCV Rack will most likely run slower than the * same code running in at right loop. Some reasons are: * - VCV's audio processing thread probably has some internal overheard, so * it's unlikely that modules will get 100% of one core. * - In a tight loop all the data you code references will probably end up in fast on-chip * cache memory. In VCV your step function is called once, then all the other module's step * functions are called. This may force your data out of cache. */ template class MeasureTime { public: MeasureTime() = delete; // we are only static /** * Executes function "func" and measures how long it takes. * Will call func in a tight look lasting minTime seconds. * When done, prints out statistics. */ static void run(const char * name, std::function func, float minTime) { int64_t iterations; bool done = false; //keep increasing the number of iterations until we last at least minTime seconds for (iterations = 100; !done; iterations *= 2) { double elapsed = measureTimeSub(func, iterations); if (elapsed >= minTime) { double itersPerSec = iterations / elapsed; double full = 44100; double percent = full * 100 / itersPerSec; printf("\nmeasure %s over time %f\n", name, minTime); printf("did %" PRId64 " iterations in %f seconds\n", iterations, elapsed); printf("that's %f per sec\n", itersPerSec); printf("percent CPU usage: %f\n", percent); printf("best case instances: %f\n", 100 / percent); printf("quota used per 1 percent : %f\n", percent * 100); fflush(stdout); done = true; } } } /** * Run test iterators time, return total seconds. */ static double measureTimeSub(std::function func, int64_t iterations) { const double t0 = SqTime::seconds(); for (int64_t i = 0; i < iterations; ++i) { const T x = func(); TestBuffers::put(x); } const double t1 = SqTime::seconds(); const double elapsed = t1 - t0; return elapsed; } }; /** * Simple producer / consumer for test data. * Serves up a precalculated list of random numbers. */ template class TestBuffers { public: static const size_t size = 60000; static void put(T x) { destData[destIndex++] = x; if (destIndex >= size) { destIndex = 0; } } static T get() { T ret = sourceData[sourceIndex++]; if (sourceIndex >= size) { sourceIndex = 0; } return ret; } // TestBuffers() { for (int i = 0; i < size; ++i) { sourceData[i] = (float) rand() / (float) RAND_MAX; } } private: static size_t sourceIndex; static size_t destIndex; static T sourceData[size]; static T destData[size]; }; template T TestBuffers::sourceData[size]; template T TestBuffers::destData[size]; template size_t TestBuffers::sourceIndex = 0; template size_t TestBuffers::destIndex = 512; /** * Simple timer implementation for running inside Visual Studio */