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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3612 - (hide 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 schoenebeck 714 /*
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 schoenebeck 3612 Copyright (C) 2005 - 2019 Christian Schoenebeck <cuse@users.sf.net>
8 schoenebeck 714 */
9    
10 schoenebeck 3612 #include "lfobench.h"
11 schoenebeck 714
12 schoenebeck 717 #include "../src/engines/common/LFOTriangleIntMath.h"
13 senkov 720 #include "../src/engines/common/LFOTriangleIntAbsMath.h"
14 schoenebeck 717 #include "../src/engines/common/LFOTriangleDiHarmonic.h"
15    
16 schoenebeck 714 // return value of this benchmark
17     // to indicate the best performing solution
18 schoenebeck 3612 #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 schoenebeck 714
24 schoenebeck 717 #if SIGNED
25 schoenebeck 3612 LFOTriangleIntMath<LFO::range_signed>* pIntLFO = NULL;
26     LFOTriangleIntAbsMath<LFO::range_signed>* pIntAbsLFO = NULL;
27     LFOTriangleDiHarmonic<LFO::range_signed>* pDiHarmonicLFO = NULL;
28 schoenebeck 717 #else // unsigned
29 schoenebeck 3612 LFOTriangleIntMath<LFO::range_unsigned>* pIntLFO = NULL;
30     LFOTriangleIntAbsMath<LFO::range_unsigned>* pIntAbsLFO = NULL;
31     LFOTriangleDiHarmonic<LFO::range_unsigned>* pDiHarmonicLFO = NULL;
32 schoenebeck 717 #endif
33    
34 schoenebeck 714 // integer math solution
35 schoenebeck 3612 double int_math(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
36 schoenebeck 714 // pro forma
37 schoenebeck 3612 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 schoenebeck 714
41     clock_t stop_time;
42     clock_t start_time = clock();
43    
44     for (int run = 0; run < RUNS; run++) {
45 schoenebeck 3118 pIntLFO->updateByMIDICtrlValue(127); // pro forma
46 schoenebeck 714 for (int i = 0; i < steps; ++i) {
47 schoenebeck 3612 //pIntLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
48 schoenebeck 717 pDestinationBuffer[i] = pIntLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
49 schoenebeck 714 }
50     }
51    
52     stop_time = clock();
53 schoenebeck 3612 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
54 schoenebeck 714 #if ! SILENT
55 schoenebeck 3612 printf("int math solution elapsed time: %.1f ms\n", elapsed_time);
56 schoenebeck 714 #endif
57    
58     return elapsed_time;
59     }
60    
61 senkov 720 // integer math abs solution
62 schoenebeck 3612 double int_math_abs(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
63 senkov 720 // pro forma
64 schoenebeck 3612 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 senkov 720
68     clock_t stop_time;
69     clock_t start_time = clock();
70    
71     for (int run = 0; run < RUNS; run++) {
72 schoenebeck 3612 pIntAbsLFO->updateByMIDICtrlValue(127); // pro forma
73 senkov 720 for (int i = 0; i < steps; ++i) {
74 schoenebeck 3612 //pIntAbsLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
75 senkov 720 pDestinationBuffer[i] = pIntAbsLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
76     }
77     }
78    
79     stop_time = clock();
80 schoenebeck 3612 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
81 senkov 720 #if ! SILENT
82 schoenebeck 3612 printf("int math abs solution elapsed time: %.1f ms\n", elapsed_time);
83 senkov 720 #endif
84    
85     return elapsed_time;
86     }
87    
88 schoenebeck 714 // 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 schoenebeck 3612 double table_lookup(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
97 schoenebeck 714 // 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 schoenebeck 3612 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
148 schoenebeck 714 #if ! SILENT
149 schoenebeck 3612 printf("Table lookup solution elapsed time: %.1f ms\n", elapsed_time);
150 schoenebeck 714 #endif
151    
152     if (pPrerenderingBuffer) delete[] pPrerenderingBuffer;
153    
154     return elapsed_time;
155     }
156     #endif
157    
158     // numeric, di-harmonic solution
159 schoenebeck 3612 double numeric_di_harmonic_solution(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
160 schoenebeck 717 // pro forma
161 schoenebeck 3612 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 schoenebeck 714
165     clock_t stop_time;
166     clock_t start_time = clock();
167    
168     for (int run = 0; run < RUNS; run++) {
169 schoenebeck 3118 pDiHarmonicLFO->updateByMIDICtrlValue(127); // pro forma
170 schoenebeck 717 for (int i = 0; i < steps; ++i) {
171 schoenebeck 3612 //pDiHarmonicLFO->updateByMIDICtrlValue(float(i)/float(steps)*127.f);
172 schoenebeck 717 pDestinationBuffer[i] = pDiHarmonicLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
173 schoenebeck 714 }
174     }
175    
176     stop_time = clock();
177 schoenebeck 3612 double elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
178 schoenebeck 714 #if ! SILENT
179 schoenebeck 3612 printf("Numeric harmonics solution elapsed time: %.1f ms\n", elapsed_time);
180 schoenebeck 714 #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 schoenebeck 3612 printf("\n");
191 schoenebeck 714 # if SIGNED
192     printf("Signed triangular wave benchmark\n");
193     # else
194     printf("Unsigned triangular wave benchmark\n");
195 schoenebeck 3612 # endif
196 schoenebeck 714 printf("----------------------------------\n");
197 schoenebeck 3612 printf("\n");
198 schoenebeck 714 #endif
199    
200 schoenebeck 717 #if SIGNED
201 schoenebeck 3612 pIntLFO = new LFOTriangleIntMath<LFO::range_signed>(MAX);
202     pIntAbsLFO = new LFOTriangleIntAbsMath<LFO::range_signed>(MAX);
203     pDiHarmonicLFO = new LFOTriangleDiHarmonic<LFO::range_signed>(MAX);
204 schoenebeck 717 #else // unsigned
205 schoenebeck 3612 pIntLFO = new LFOTriangleIntMath<LFO::range_unsigned>(MAX);
206     pIntAbsLFO = new LFOTriangleIntAbsMath<LFO::range_unsigned>(MAX);
207     pDiHarmonicLFO = new LFOTriangleDiHarmonic<LFO::range_unsigned>(MAX);
208 schoenebeck 717 #endif
209    
210 schoenebeck 714 // output buffer for the calculated sinusoid wave
211 schoenebeck 3054 smpl_t* pOutputBuffer = new smpl_t[steps];
212 schoenebeck 714 // 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 schoenebeck 3612 // going to store how long each solution took (in seconds)
220     std::vector<BenchRes> results;
221 schoenebeck 714
222 schoenebeck 3612
223     results.push_back({
224     .algorithmID = TRIANG_INT_MATH_SOLUTION,
225     .algorithmName = "int math",
226     .timeMSecs = int_math(pOutputBuffer, pAmplitude, steps, sinusoidFrequency)
227     });
228 schoenebeck 714 #if OUTPUT_AS_RAW_WAVE
229 schoenebeck 3612 output_as_raw_file("bench_int_math.raw", pOutputBuffer, steps);
230 schoenebeck 714 #endif
231 senkov 720
232 schoenebeck 3612
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 senkov 720 #if OUTPUT_AS_RAW_WAVE
239 schoenebeck 3612 output_as_raw_file("bench_int_math_abs.raw", pOutputBuffer, steps);
240 senkov 720 #endif
241 schoenebeck 3612
242    
243 schoenebeck 714 //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 schoenebeck 3612
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 schoenebeck 714 #if OUTPUT_AS_RAW_WAVE
255 schoenebeck 3612 output_as_raw_file("bench_numeric_harmonics.raw", pOutputBuffer, steps);
256 schoenebeck 714 #endif
257    
258 schoenebeck 3612
259 schoenebeck 714 #if ! SILENT
260     printf("\nOK, 2nd try\n\n");
261     #endif
262    
263    
264 schoenebeck 3612 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 schoenebeck 714 if (pAmplitude) delete[] pAmplitude;
271     if (pOutputBuffer) delete[] pOutputBuffer;
272    
273 schoenebeck 717 if (pIntLFO) delete pIntLFO;
274 schoenebeck 3612 if (pIntAbsLFO) delete pIntAbsLFO;
275 schoenebeck 717 if (pDiHarmonicLFO) delete pDiHarmonicLFO;
276    
277 schoenebeck 3612 sortResultsFirstToBeBest(results);
278     printResultSummary(results);
279 schoenebeck 714
280 schoenebeck 3612 return results[0].algorithmID; // return the winner's numeric algorithm ID
281 schoenebeck 714 }

  ViewVC Help
Powered by ViewVC