Created
February 5, 2019 17:43
-
-
Save jthistle/233f9876a1ee6da3bb4c1f1e20d328e6 to your computer and use it in GitHub Desktop.
Benchmarks different methods for single note dynamics in MuseScore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <functional> | |
#include <math.h> | |
#include <sys/time.h> | |
#include <ctime> | |
namespace Bm | |
{ | |
void benchmark1(int method) | |
{ | |
int startExpr = 0; | |
int endExpr = 100; | |
int exprDiff = endExpr-startExpr; | |
int exprTicks = 100; | |
int tickInc = 1; | |
std::function<int(std::vector<double>&, int)> valueFunction; | |
std::vector<double> preCalculated; | |
switch (method) { | |
case 0: | |
// Due to the nth-root, exponential functions do not flip with negative values, and cause errors, | |
// so treat it as a piecewise function. | |
if (exprDiff > 0) { | |
preCalculated.push_back( | |
pow((exprDiff + 1), 1.0 / double(exprTicks)) // the exprTicks root of d+1 | |
); | |
valueFunction = [](std::vector<double>& pc, int ct) { return int( | |
pow( | |
pc[0], | |
double(ct) // to the power of the current tick (exponential) | |
) - 1 | |
); }; | |
} | |
else { | |
preCalculated.push_back( | |
pow((-exprDiff + 1), 1.0 / double(exprTicks)) // the exprTicks root of 1-d | |
); | |
valueFunction = [](std::vector<double>& pc, int ct) { return -int( | |
pow( | |
pc[0], | |
double(ct) // again to the power of ct | |
) + 1 | |
); }; | |
} | |
break; | |
// Uses sin x transformed, which _does_ flip with negative numbers | |
case 1: | |
preCalculated.push_back(double(exprDiff) / 2.0); | |
preCalculated.push_back(double(M_PI / double(exprTicks))); | |
preCalculated.push_back(double(M_PI / 2.0)); | |
valueFunction = [](std::vector<double>& pc, int ct) { return int( | |
pc[0] * ( | |
sin( | |
double(ct) * ( | |
pc[1] | |
) - pc[2] | |
) + 1 | |
) | |
); }; | |
break; | |
case 2: | |
preCalculated.push_back(double(exprDiff)); | |
preCalculated.push_back(double(exprTicks)); | |
preCalculated.push_back(double(M_PI / double(2 * exprTicks))); | |
valueFunction = [](std::vector<double>& pc, int ct) { return int( | |
pc[0] * ( | |
sin( | |
double(ct - pc[1]) * ( | |
pc[2] | |
) | |
) + 1 | |
) | |
); }; | |
break; | |
case 3: | |
preCalculated.push_back(double(exprDiff)); | |
preCalculated.push_back(double(M_PI / double(2 * exprTicks))); | |
valueFunction = [](std::vector<double>& pc, int ct) { return int( | |
pc[0] * sin( | |
double(ct) * ( | |
pc[1] | |
) | |
) | |
); }; | |
break; | |
case 4: | |
default: | |
// We can calculate how to increase the ticks, since it is linear | |
tickInc = exprTicks / abs(exprDiff); | |
preCalculated.push_back(double(exprDiff)); | |
preCalculated.push_back(double(exprTicks)); | |
valueFunction = [](std::vector<double>& pc, int ct) { return int(pc[0] * (double(ct) / pc[1])); }; | |
break; | |
} | |
// prevent possible infinite loop | |
if (tickInc < 1) | |
tickInc = 1; | |
int lastVal = -1; | |
for (int i = 0; i < exprTicks; i += tickInc) { | |
int valueToAdd = valueFunction(preCalculated, i); | |
if (lastVal == valueToAdd) | |
continue; | |
lastVal = valueToAdd; | |
int exprVal = startExpr + valueToAdd; | |
} | |
} | |
void benchmark2(int method) | |
{ | |
int startExpr = 0; | |
int endExpr = 100; | |
int exprDiff = endExpr-startExpr; | |
int exprTicks = 100; | |
int tickInc = 1; | |
// See these functions graphically at: https://www.desmos.com/calculator/kk89ficmjk | |
std::function<int(int,int,int)> valueFunction; | |
switch (method) { | |
case 0: | |
// Due to the nth-root, exponential functions do not flip with negative values, and cause errors, | |
// so treat it as a piecewise function. | |
if (exprDiff > 0) | |
valueFunction = [](int d, int ct, int tt) { return int( | |
pow( | |
pow((d + 1), 1.0 / double(tt)), // the tt root of d+1 | |
double(ct) // to the power of the current tick (exponential) | |
) - 1 | |
); }; | |
else | |
valueFunction = [](int d, int ct, int tt) { return -int( | |
pow( | |
pow((-d + 1), 1.0 / double(tt)), // the tt root of 1 - d | |
double(ct) // again to the power of ct | |
) + 1 | |
); }; | |
break; | |
// Uses sin x transformed, which _does_ flip with negative numbers | |
case 1: | |
valueFunction = [](int d, int ct, int tt) { return int( | |
(double(d) / 2.0) * ( | |
sin( | |
double(ct) * ( | |
M_PI / double(tt) | |
) - M_PI / 2.0 | |
) + 1 | |
) | |
); }; | |
break; | |
case 2: | |
valueFunction = [](int d, int ct, int tt) { return int( | |
double(d) * ( | |
sin( | |
double(ct - tt) * ( | |
M_PI / double(2 * tt) | |
) | |
) + 1 | |
) | |
); }; | |
break; | |
case 3: | |
valueFunction = [](int d, int ct, int tt) { return int( | |
double(d) * sin( | |
double(ct) * ( | |
M_PI / double(2 * tt) | |
) | |
) | |
); }; | |
break; | |
case 4: | |
default: | |
// We can calculate how to increase the ticks, since it is linear | |
tickInc = exprTicks / abs(exprDiff); | |
valueFunction = [](int d, int ct, int tt) { return int(d * (double(ct) / double(tt))); }; | |
break; | |
} | |
// prevent possible infinite loop | |
if (tickInc < 1) | |
tickInc = 1; | |
int lastVal = -1; | |
for (int i = 0; i < exprTicks; i += tickInc) { | |
int valueToAdd = valueFunction(exprDiff, i, exprTicks); | |
if (lastVal == valueToAdd) | |
continue; | |
lastVal = valueToAdd; | |
int exprVal = startExpr + valueToAdd; | |
} | |
} | |
} | |
/* Remove if already defined */ | |
typedef long long int64; typedef unsigned long long uint64; | |
/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both | |
* windows and linux. */ | |
uint64 GetTimeMs64() | |
{ | |
struct timeval tv; | |
gettimeofday(&tv, NULL); | |
uint64 ret = tv.tv_usec; | |
/* Convert from micro seconds (10^-6) to milliseconds (10^-3) */ | |
ret /= 1000; | |
/* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */ | |
ret += (tv.tv_sec * 1000); | |
return ret; | |
} | |
int main() | |
{ | |
const int testCount = 1000000; | |
const int repeats = 5; | |
std::cout << "Running benchmark: " << testCount << " iterations, " << repeats << " repeats" << std::endl; | |
std::vector<double> pImp; | |
for (int r = 0; r < repeats; ++r) { | |
uint64 st, et; | |
int m1, m2; | |
st = GetTimeMs64(); | |
for (int i = 0; i <= testCount; ++i) | |
Bm::benchmark1(i%5); | |
et = GetTimeMs64(); | |
m1 = et-st; | |
std::cout << "Method 1 (new): " << m1 << "ms" << std::endl; | |
st = GetTimeMs64(); | |
for (int i = 0; i < testCount; ++i) | |
Bm::benchmark2(i%5); | |
et = GetTimeMs64(); | |
m2 = et-st; | |
std::cout << "Method 2: " << m2 << "ms" << std::endl; | |
double p = (double(m2-m1)/double(m2))*100; | |
pImp.push_back(p); | |
std::cout << "Method 1 runs " << p << "% faster than m2" << std::endl; | |
} | |
double avg; | |
for (int p : pImp) avg += p; | |
avg /= double(pImp.size()); | |
std::cout << "Average percentage increase in performance: " << avg << "%" << std::endl; | |
return 1; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment