/[svn]/linuxsampler/trunk/benchmarks/triang.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/benchmarks/triang.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3612 - (show annotations) (download)
Mon Sep 30 18:03:43 2019 UTC (4 years, 6 months ago) by schoenebeck
File size: 9927 byte(s)
Added new LFO implementations:

* Added int math square LFO implementation.

* Added int math saw LFO implementation.

* Added numeric complex nr sine LFO implementation.

* Added public API C++ class "LFO", which is a cluster class
  encapsulating all the sampler's LFO implementations to be used by
  3rd party applications (e.g. by Gigedit).

* Marked class LFOTriangleDiHarmonic as deprecated
  (will be removed in future).

* Added LFOAll.h which includes all LFO implementation's header files.

* Fixed benchmarks/triang.cpp falsely having favoured "int math abs"
  algorithm (since result of 2nd run was not accumulated).

* Added benchmark for saw wave (benchmarks/saw.cpp).

* Added benchmark for sine wave (benchmarks/sine.cpp).

* Added benchmark for square wave (benchmarks/square.cpp).

* Increased amount of benchmarks runs by factor 6 to achieve benchmark
  times which are large enough on modern systems.

* Cleanup of LFO APIs.

* Bumped version (2.1.1.svn18).

1 /*
2 Triangle wave generator benchmark
3
4 This is a benchmark for comparison between a integer math, table lookup
5 and numeric sine wave harmonics solution.
6
7 Copyright (C) 2005 - 2019 Christian Schoenebeck <cuse@users.sf.net>
8 */
9
10 #include "lfobench.h"
11
12 #include "../src/engines/common/LFOTriangleIntMath.h"
13 #include "../src/engines/common/LFOTriangleIntAbsMath.h"
14 #include "../src/engines/common/LFOTriangleDiHarmonic.h"
15
16 // return value of this benchmark
17 // to indicate the best performing solution
18 #define TRIANG_INT_MATH_SOLUTION 2 /* we don't start with 1, as this is reserved for unknown errors */
19 #define TRIANG_DI_HARMONIC_SOLUTION 3
20 #define TRIANG_TABLE_LOOKUP_SOLUTION 4 /* table lookup solution is currently disabled in this benchmark, see below */
21 #define TRIANG_INT_MATH_ABS_SOLUTION 5 /* integer math with abs() */
22 #define INVALID_RESULT -1
23
24 #if SIGNED
25 LFOTriangleIntMath<LFO::range_signed>* pIntLFO = NULL;
26 LFOTriangleIntAbsMath<LFO::range_signed>* pIntAbsLFO = NULL;
27 LFOTriangleDiHarmonic<LFO::range_signed>* pDiHarmonicLFO = NULL;
28 #else // unsigned
29 LFOTriangleIntMath<LFO::range_unsigned>* pIntLFO = NULL;
30 LFOTriangleIntAbsMath<LFO::range_unsigned>* pIntAbsLFO = NULL;
31 LFOTriangleDiHarmonic<LFO::range_unsigned>* pDiHarmonicLFO = NULL;
32 #endif
33
34 // integer math solution
35 double int_math(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
36 // pro forma
37 pIntLFO->trigger(frequency, LFO::start_level_min, 0 /* max. internal depth */, 1200, false, (unsigned int) SAMPLING_RATE);
38 //pIntLFO->setPhase(0);
39 //pIntLFO->setFrequency(frequency*2, SAMPLING_RATE);
40
41 clock_t stop_time;
42 clock_t start_time = clock();
43
44 for (int run = 0; run < RUNS; run++) {
45 pIntLFO->updateByMIDICtrlValue(127); // pro forma
46 for (int i = 0; i < steps; ++i) {
47 //pIntLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
48 pDestinationBuffer[i] = pIntLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
49 }
50 }
51
52 stop_time = clock();
53 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
54 #if ! SILENT
55 printf("int math solution elapsed time: %.1f ms\n", elapsed_time);
56 #endif
57
58 return elapsed_time;
59 }
60
61 // integer math abs solution
62 double int_math_abs(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
63 // pro forma
64 pIntAbsLFO->trigger(frequency, LFO::start_level_min, 0 /* max. internal depth */, 1200, false, (unsigned int) SAMPLING_RATE);
65 //pIntAbsLFO->setPhase(0);
66 //pIntAbsLFO->setFrequency(frequency*2, SAMPLING_RATE);
67
68 clock_t stop_time;
69 clock_t start_time = clock();
70
71 for (int run = 0; run < RUNS; run++) {
72 pIntAbsLFO->updateByMIDICtrlValue(127); // pro forma
73 for (int i = 0; i < steps; ++i) {
74 //pIntAbsLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
75 pDestinationBuffer[i] = pIntAbsLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
76 }
77 }
78
79 stop_time = clock();
80 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
81 #if ! SILENT
82 printf("int math abs solution elapsed time: %.1f ms\n", elapsed_time);
83 #endif
84
85 return elapsed_time;
86 }
87
88 // table lookup solution (currently disabled)
89 //
90 // This solution is not yet implemented in LinuxSampler yet and probably
91 // never will, I simply haven't found an architectures / system where this
92 // turned out to be the best solution and it introduces too many problems
93 // anyway. If you found an architecture where this seems to be the best
94 // solution, please let us know!
95 #if 0
96 double table_lookup(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
97 // pro forma
98 const float r = frequency / SAMPLING_RATE; // frequency alteration quotient
99 #if SIGNED
100 float c = r * 4.0f;
101 #else
102 float c = r * 2.0f;
103 #endif
104 const int wl = (int) (SAMPLING_RATE / frequency); // wave length (in sample points)
105
106 // 'volatile' to avoid the cache to fudge the benchmark result
107 volatile float* pPrerenderingBuffer = new float[wl];
108
109 // prerendering of the triangular wave
110 {
111 float level = 0.0f;
112 for (int i = 0; i < wl; ++i) {
113 level += c;
114 #if SIGNED
115 if (level >= 1.0f) {
116 c = -c;
117 level = 1.0f;
118 }
119 else if (level <= -1.0f) {
120 c = -c;
121 level = -1.0f;
122 }
123 #else
124 if (level >= 1.0f) {
125 c = -c;
126 level = 1.0f;
127 }
128 else if (level <= 0.0f) {
129 c = -c;
130 level = 0.0f;
131 }
132 #endif
133 pPrerenderingBuffer[i] = level;
134 }
135 }
136
137 clock_t stop_time;
138 clock_t start_time = clock();
139
140 for (int run = 0; run < RUNS; run++) {
141 for (int i = 0; i < steps; ++i) {
142 pDestinationBuffer[i] = pPrerenderingBuffer[i % wl] * pAmp[i]; // * pAmp[i] just to simulate some memory load
143 }
144 }
145
146 stop_time = clock();
147 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
148 #if ! SILENT
149 printf("Table lookup solution elapsed time: %.1f ms\n", elapsed_time);
150 #endif
151
152 if (pPrerenderingBuffer) delete[] pPrerenderingBuffer;
153
154 return elapsed_time;
155 }
156 #endif
157
158 // numeric, di-harmonic solution
159 double numeric_di_harmonic_solution(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
160 // pro forma
161 pDiHarmonicLFO->trigger(frequency, LFO::start_level_min, 0 /* max. internal depth */, 1200, false, (unsigned int) SAMPLING_RATE);
162 //pDiHarmonicLFO->setPhase(0);
163 //pDiHarmonicLFO->setFrequency(frequency*2, SAMPLING_RATE);
164
165 clock_t stop_time;
166 clock_t start_time = clock();
167
168 for (int run = 0; run < RUNS; run++) {
169 pDiHarmonicLFO->updateByMIDICtrlValue(127); // pro forma
170 for (int i = 0; i < steps; ++i) {
171 //pDiHarmonicLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
172 pDestinationBuffer[i] = pDiHarmonicLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
173 }
174 }
175
176 stop_time = clock();
177 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
178 #if ! SILENT
179 printf("Numeric harmonics solution elapsed time: %.1f ms\n", elapsed_time);
180 #endif
181
182 return elapsed_time;
183 }
184
185 int main() {
186 const int steps = STEPS;
187 const int sinusoidFrequency = 100; // Hz
188
189 #if ! SILENT
190 printf("\n");
191 # if SIGNED
192 printf("Signed triangular wave benchmark\n");
193 # else
194 printf("Unsigned triangular wave benchmark\n");
195 # endif
196 printf("----------------------------------\n");
197 printf("\n");
198 #endif
199
200 #if SIGNED
201 pIntLFO = new LFOTriangleIntMath<LFO::range_signed>(MAX);
202 pIntAbsLFO = new LFOTriangleIntAbsMath<LFO::range_signed>(MAX);
203 pDiHarmonicLFO = new LFOTriangleDiHarmonic<LFO::range_signed>(MAX);
204 #else // unsigned
205 pIntLFO = new LFOTriangleIntMath<LFO::range_unsigned>(MAX);
206 pIntAbsLFO = new LFOTriangleIntAbsMath<LFO::range_unsigned>(MAX);
207 pDiHarmonicLFO = new LFOTriangleDiHarmonic<LFO::range_unsigned>(MAX);
208 #endif
209
210 // output buffer for the calculated sinusoid wave
211 smpl_t* pOutputBuffer = new smpl_t[steps];
212 // just an arbitrary amplitude envelope to simulate a bit higher memory bandwidth
213 float* pAmplitude = new float[steps];
214
215 // pro forma - an arbitary amplitude envelope
216 for (int i = 0; i < steps; ++i)
217 pAmplitude[i] = (float) i / (float) steps;
218
219 // going to store how long each solution took (in seconds)
220 std::vector<BenchRes> results;
221
222
223 results.push_back({
224 .algorithmID = TRIANG_INT_MATH_SOLUTION,
225 .algorithmName = "int math",
226 .timeMSecs = int_math(pOutputBuffer, pAmplitude, steps, sinusoidFrequency)
227 });
228 #if OUTPUT_AS_RAW_WAVE
229 output_as_raw_file("bench_int_math.raw", pOutputBuffer, steps);
230 #endif
231
232
233 results.push_back({
234 .algorithmID = TRIANG_INT_MATH_ABS_SOLUTION,
235 .algorithmName = "int math abs",
236 .timeMSecs = int_math_abs(pOutputBuffer, pAmplitude, steps, sinusoidFrequency)
237 });
238 #if OUTPUT_AS_RAW_WAVE
239 output_as_raw_file("bench_int_math_abs.raw", pOutputBuffer, steps);
240 #endif
241
242
243 //table_lookup_result = table_lookup(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
244 //#if OUTPUT_AS_RAW_WAVE
245 // output_as_raw_file("bench_table_lookup.raw", pOutputBuffer, steps);
246 //#endif
247
248
249 results.push_back({
250 .algorithmID = TRIANG_DI_HARMONIC_SOLUTION,
251 .algorithmName = "Numeric di harmonic",
252 .timeMSecs = numeric_di_harmonic_solution(pOutputBuffer, pAmplitude, steps, sinusoidFrequency)
253 });
254 #if OUTPUT_AS_RAW_WAVE
255 output_as_raw_file("bench_numeric_harmonics.raw", pOutputBuffer, steps);
256 #endif
257
258
259 #if ! SILENT
260 printf("\nOK, 2nd try\n\n");
261 #endif
262
263
264 results[0].timeMSecs += int_math(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
265 results[1].timeMSecs += int_math_abs(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
266 //table_lookup_result += table_lookup(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
267 results[2].timeMSecs += numeric_di_harmonic_solution(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
268
269
270 if (pAmplitude) delete[] pAmplitude;
271 if (pOutputBuffer) delete[] pOutputBuffer;
272
273 if (pIntLFO) delete pIntLFO;
274 if (pIntAbsLFO) delete pIntAbsLFO;
275 if (pDiHarmonicLFO) delete pDiHarmonicLFO;
276
277 sortResultsFirstToBeBest(results);
278 printResultSummary(results);
279
280 return results[0].algorithmID; // return the winner's numeric algorithm ID
281 }

  ViewVC Help
Powered by ViewVC