Chris@3
|
1
|
Chris@26
|
2 /* Utility functions to generate arbitrary input in various formats */
|
Chris@3
|
3
|
Chris@3
|
4 function inputReals(size) {
|
Chris@8
|
5 var result = new Float32Array(size);
|
Chris@3
|
6 for (var i = 0; i < result.length; i++)
|
Chris@26
|
7 result[i] = (i % 2) / 4.0;
|
Chris@3
|
8 return result;
|
Chris@3
|
9 }
|
Chris@3
|
10
|
Chris@3
|
11 function zeroReals(size) {
|
Chris@8
|
12 var result = new Float32Array(size);
|
Chris@3
|
13 for (var i = 0; i < result.length; i++)
|
Chris@3
|
14 result[i] = 0.0;
|
Chris@3
|
15 return result;
|
Chris@3
|
16 }
|
Chris@3
|
17
|
Chris@37
|
18 function inputInterleaved(size) {
|
Chris@37
|
19 var result = new Float32Array(size*2);
|
Chris@37
|
20 for (var i = 0; i < size; i++)
|
Chris@37
|
21 result[i*2] = (i % 2) / 4.0;
|
Chris@37
|
22 return result;
|
Chris@37
|
23 }
|
Chris@37
|
24
|
Chris@3
|
25 function inputReal64s(size) {
|
Chris@3
|
26 var result = new Float64Array(size);
|
Chris@3
|
27 for (var i = 0; i < result.length; i++)
|
Chris@26
|
28 result[i] = (i % 2) / 4.0;
|
Chris@3
|
29 return result;
|
Chris@3
|
30 }
|
Chris@3
|
31
|
Chris@7
|
32 function zeroReal64s(size) {
|
Chris@7
|
33 var result = new Float64Array(size);
|
Chris@7
|
34 for (var i = 0; i < result.length; i++)
|
Chris@26
|
35 result[i] = 0.0;
|
Chris@7
|
36 return result;
|
Chris@7
|
37 }
|
Chris@7
|
38
|
Chris@3
|
39 function inputComplexArray(size) {
|
Chris@3
|
40 var result = new complex_array.ComplexArray(size);
|
Chris@3
|
41 for (var i = 0; i < size; i++) {
|
Chris@26
|
42 result.real[i] = (i % 2) / 4.0;
|
Chris@3
|
43 result.imag[i] = 0.0;
|
Chris@3
|
44 }
|
Chris@3
|
45 return result;
|
Chris@3
|
46 }
|
Chris@3
|
47
|
Chris@19
|
48 var iterations = 2000;
|
Chris@3
|
49
|
Chris@3
|
50 function report(name, start, middle, end, total) {
|
Chris@19
|
51 function addTo(tag, thing) {
|
Chris@19
|
52 document.getElementById(name + "-" + tag).innerHTML += thing + "<br>";
|
Chris@19
|
53 }
|
Chris@19
|
54 addTo("result", total);
|
Chris@19
|
55 addTo("1", Math.round(middle - start) + " ms");
|
Chris@19
|
56 addTo("2", Math.round(end - middle) + " ms");
|
Chris@19
|
57 addTo("itr", Math.round((1000.0 /
|
Chris@19
|
58 ((end - middle) / iterations))) + " itr/sec");
|
Chris@3
|
59 }
|
Chris@3
|
60
|
Chris@19
|
61 function testNayuki(size) {
|
Chris@3
|
62
|
Chris@3
|
63 var start = performance.now();
|
Chris@3
|
64 var middle = start;
|
Chris@3
|
65 var end = start;
|
Chris@3
|
66
|
Chris@3
|
67 var total = 0.0;
|
Chris@3
|
68
|
Chris@3
|
69 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@3
|
70 if (i == iterations) {
|
Chris@3
|
71 middle = performance.now();
|
Chris@3
|
72 }
|
Chris@3
|
73 var real = inputReals(size);
|
Chris@3
|
74 var imag = zeroReals(size);
|
Chris@3
|
75 transform(real, imag);
|
Chris@3
|
76 for (var j = 0; j < size; ++j) {
|
Chris@4
|
77 total += Math.sqrt(real[j] * real[j] + imag[j] * imag[j]);
|
Chris@3
|
78 }
|
Chris@3
|
79 }
|
Chris@3
|
80
|
Chris@3
|
81 var end = performance.now();
|
Chris@3
|
82
|
Chris@3
|
83 report("nayuki", start, middle, end, total);
|
Chris@3
|
84 }
|
Chris@3
|
85
|
Chris@19
|
86 function testNayukiObj(size) {
|
Chris@17
|
87
|
Chris@17
|
88 var fft = new FFTNayuki(size);
|
Chris@17
|
89
|
Chris@17
|
90 var start = performance.now();
|
Chris@17
|
91 var middle = start;
|
Chris@17
|
92 var end = start;
|
Chris@17
|
93
|
Chris@17
|
94 var total = 0.0;
|
Chris@17
|
95
|
Chris@17
|
96 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@17
|
97 if (i == iterations) {
|
Chris@17
|
98 middle = performance.now();
|
Chris@17
|
99 }
|
Chris@17
|
100 var real = inputReals(size);
|
Chris@17
|
101 var imag = zeroReals(size);
|
Chris@17
|
102 fft.forward(real, imag);
|
Chris@17
|
103 for (var j = 0; j < size; ++j) {
|
Chris@17
|
104 total += Math.sqrt(real[j] * real[j] + imag[j] * imag[j]);
|
Chris@17
|
105 }
|
Chris@17
|
106 }
|
Chris@17
|
107
|
Chris@17
|
108 var end = performance.now();
|
Chris@17
|
109
|
Chris@17
|
110 report("nayukiobj", start, middle, end, total);
|
Chris@17
|
111 }
|
Chris@17
|
112
|
Chris@32
|
113 function testNayukiC(size) {
|
Chris@32
|
114
|
Chris@32
|
115 var fft = new FFTNayukiC(size);
|
Chris@32
|
116
|
Chris@32
|
117 var start = performance.now();
|
Chris@32
|
118 var middle = start;
|
Chris@32
|
119 var end = start;
|
Chris@32
|
120
|
Chris@32
|
121 total = 0.0;
|
Chris@32
|
122
|
Chris@32
|
123 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@32
|
124 if (i == iterations) {
|
Chris@32
|
125 middle = performance.now();
|
Chris@32
|
126 }
|
Chris@33
|
127 var real = inputReal64s(size);
|
Chris@33
|
128 var imag = zeroReal64s(size);
|
Chris@32
|
129 fft.forward(real, imag);
|
Chris@32
|
130 for (var j = 0; j < size; ++j) {
|
Chris@32
|
131 total += Math.sqrt(real[j] * real[j] + imag[j] * imag[j]);
|
Chris@32
|
132 }
|
Chris@32
|
133 }
|
Chris@32
|
134
|
Chris@32
|
135 var end = performance.now();
|
Chris@32
|
136
|
Chris@32
|
137 fft.dispose();
|
Chris@32
|
138
|
Chris@32
|
139 report("nayukic", start, middle, end, total);
|
Chris@32
|
140 }
|
Chris@32
|
141
|
Chris@37
|
142 function testNayukiCF(size) {
|
Chris@37
|
143
|
Chris@37
|
144 var fft = new FFTNayukiCFloat(size);
|
Chris@37
|
145
|
Chris@37
|
146 var start = performance.now();
|
Chris@37
|
147 var middle = start;
|
Chris@37
|
148 var end = start;
|
Chris@37
|
149
|
Chris@37
|
150 total = 0.0;
|
Chris@37
|
151
|
Chris@37
|
152 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@37
|
153 if (i == iterations) {
|
Chris@37
|
154 middle = performance.now();
|
Chris@37
|
155 }
|
Chris@37
|
156 var real = inputReals(size);
|
Chris@37
|
157 var imag = zeroReals(size);
|
Chris@37
|
158 fft.forward(real, imag);
|
Chris@37
|
159 for (var j = 0; j < size; ++j) {
|
Chris@37
|
160 total += Math.sqrt(real[j] * real[j] + imag[j] * imag[j]);
|
Chris@37
|
161 }
|
Chris@37
|
162 }
|
Chris@37
|
163
|
Chris@37
|
164 var end = performance.now();
|
Chris@37
|
165
|
Chris@37
|
166 fft.dispose();
|
Chris@37
|
167
|
Chris@37
|
168 report("nayukicf", start, middle, end, total);
|
Chris@37
|
169 }
|
Chris@37
|
170
|
Chris@19
|
171 function testNockert(size) {
|
Chris@3
|
172
|
Chris@3
|
173 var fft = new FFT.complex(size, false);
|
Chris@3
|
174
|
Chris@3
|
175 var start = performance.now();
|
Chris@3
|
176 var middle = start;
|
Chris@3
|
177 var end = start;
|
Chris@3
|
178
|
Chris@3
|
179 total = 0.0;
|
Chris@3
|
180
|
Chris@3
|
181 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@3
|
182 if (i == iterations) {
|
Chris@3
|
183 middle = performance.now();
|
Chris@3
|
184 }
|
Chris@3
|
185 var ri = inputReal64s(size);
|
Chris@3
|
186 var co = new Float64Array(2 * size);
|
Chris@3
|
187 fft.simple(co, ri, 'real');
|
Chris@4
|
188 for (var j = 0; j < size; ++j) {
|
Chris@4
|
189 total += Math.sqrt(co[j*2] * co[j*2] + co[j*2+1] * co[j*2+1]);
|
Chris@3
|
190 }
|
Chris@3
|
191 }
|
Chris@3
|
192
|
Chris@3
|
193 var end = performance.now();
|
Chris@3
|
194
|
Chris@3
|
195 report("nockert", start, middle, end, total);
|
Chris@3
|
196 }
|
Chris@3
|
197
|
Chris@19
|
198 function testDntj(size) {
|
Chris@3
|
199
|
Chris@3
|
200 var start = performance.now();
|
Chris@3
|
201 var middle = start;
|
Chris@3
|
202 var end = start;
|
Chris@3
|
203
|
Chris@3
|
204 total = 0.0;
|
Chris@4
|
205 var scale = Math.sqrt(size);
|
Chris@3
|
206
|
Chris@3
|
207 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@3
|
208 if (i == iterations) {
|
Chris@3
|
209 middle = performance.now();
|
Chris@3
|
210 }
|
Chris@3
|
211 var ci = inputComplexArray(size);
|
Chris@3
|
212 var co = ci.FFT();
|
Chris@3
|
213 for (var j = 0; j < size; ++j) {
|
Chris@4
|
214 total += scale *
|
Chris@4
|
215 Math.sqrt(co.real[j] * co.real[j] + co.imag[j] * co.imag[j]);
|
Chris@3
|
216 }
|
Chris@3
|
217 }
|
Chris@3
|
218
|
Chris@3
|
219 var end = performance.now();
|
Chris@3
|
220
|
Chris@4
|
221 report("dntj", start, middle, end, total);
|
Chris@3
|
222 }
|
Chris@3
|
223
|
Chris@19
|
224 function testCross(size) {
|
Chris@7
|
225
|
Chris@7
|
226 var fft = new FFTCross(size);
|
Chris@7
|
227
|
Chris@7
|
228 var start = performance.now();
|
Chris@7
|
229 var middle = start;
|
Chris@7
|
230 var end = start;
|
Chris@7
|
231
|
Chris@7
|
232 total = 0.0;
|
Chris@7
|
233
|
Chris@7
|
234 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@7
|
235 if (i == iterations) {
|
Chris@7
|
236 middle = performance.now();
|
Chris@7
|
237 }
|
Chris@7
|
238 var ri = inputReal64s(size);
|
Chris@7
|
239 var out = fft.transformReal(ri, false);
|
Chris@7
|
240 for (var j = 0; j < size; ++j) {
|
Chris@7
|
241 total +=
|
Chris@7
|
242 Math.sqrt(out.real[j] * out.real[j] + out.imag[j] * out.imag[j]);
|
Chris@7
|
243 }
|
Chris@7
|
244 }
|
Chris@7
|
245
|
Chris@7
|
246 var end = performance.now();
|
Chris@8
|
247
|
Chris@8
|
248 report("cross", start, middle, end, total);
|
Chris@7
|
249
|
Chris@19
|
250 fft.dispose();
|
Chris@8
|
251 }
|
Chris@8
|
252
|
Chris@19
|
253 function testKissFFT(size) {
|
Chris@8
|
254
|
Chris@37
|
255 var fft = new KissFFTR(size);
|
Chris@8
|
256
|
Chris@8
|
257 var start = performance.now();
|
Chris@8
|
258 var middle = start;
|
Chris@8
|
259 var end = start;
|
Chris@8
|
260
|
Chris@8
|
261 total = 0.0;
|
Chris@8
|
262
|
Chris@8
|
263 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@8
|
264 if (i == iterations) {
|
Chris@8
|
265 middle = performance.now();
|
Chris@8
|
266 }
|
Chris@8
|
267 var ri = inputReals(size);
|
Chris@8
|
268 var out = fft.forward(ri);
|
Chris@8
|
269 for (var j = 0; j <= size/2; ++j) {
|
Chris@8
|
270 total += Math.sqrt(out[j*2] * out[j*2] + out[j*2+1] * out[j*2+1]);
|
Chris@8
|
271 }
|
Chris@37
|
272 // KissFFTR returns only the first half of the output (plus
|
Chris@8
|
273 // DC/Nyquist) -- synthesise the conjugate half
|
Chris@8
|
274 for (var j = 1; j < size/2; ++j) {
|
Chris@8
|
275 total += Math.sqrt(out[j*2] * out[j*2] + out[j*2+1] * out[j*2+1]);
|
Chris@8
|
276 }
|
Chris@8
|
277 }
|
Chris@8
|
278
|
Chris@8
|
279 var end = performance.now();
|
Chris@8
|
280
|
Chris@8
|
281 report("kissfft", start, middle, end, total);
|
Chris@8
|
282
|
Chris@19
|
283 fft.dispose();
|
Chris@7
|
284 }
|
Chris@7
|
285
|
Chris@37
|
286 function testKissFFTCC(size) {
|
Chris@37
|
287
|
Chris@37
|
288 var fft = new KissFFT(size);
|
Chris@37
|
289
|
Chris@37
|
290 var start = performance.now();
|
Chris@37
|
291 var middle = start;
|
Chris@37
|
292 var end = start;
|
Chris@37
|
293
|
Chris@37
|
294 total = 0.0;
|
Chris@37
|
295
|
Chris@37
|
296 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@37
|
297 if (i == iterations) {
|
Chris@37
|
298 middle = performance.now();
|
Chris@37
|
299 }
|
Chris@37
|
300 var cin = inputInterleaved(size);
|
Chris@37
|
301 var out = fft.forward(cin);
|
Chris@37
|
302 for (var j = 0; j < size; ++j) {
|
Chris@37
|
303 total += Math.sqrt(out[j*2] * out[j*2] + out[j*2+1] * out[j*2+1]);
|
Chris@37
|
304 }
|
Chris@37
|
305 }
|
Chris@37
|
306
|
Chris@37
|
307 var end = performance.now();
|
Chris@37
|
308
|
Chris@37
|
309 report("kissfftcc", start, middle, end, total);
|
Chris@37
|
310
|
Chris@37
|
311 fft.dispose();
|
Chris@37
|
312 }
|
Chris@37
|
313
|
Chris@19
|
314 function testFFTW(size) {
|
Chris@19
|
315
|
Chris@19
|
316 var fft = new FFTW(size);
|
Chris@19
|
317
|
Chris@19
|
318 var start = performance.now();
|
Chris@19
|
319 var middle = start;
|
Chris@19
|
320 var end = start;
|
Chris@19
|
321
|
Chris@19
|
322 total = 0.0;
|
Chris@19
|
323
|
Chris@19
|
324 for (var i = 0; i < 2*iterations; ++i) {
|
Chris@19
|
325 if (i == iterations) {
|
Chris@19
|
326 middle = performance.now();
|
Chris@19
|
327 }
|
Chris@19
|
328 var ri = inputReals(size);
|
Chris@19
|
329 var out = fft.forward(ri);
|
Chris@19
|
330 for (var j = 0; j <= size/2; ++j) {
|
Chris@19
|
331 total += Math.sqrt(out[j*2] * out[j*2] + out[j*2+1] * out[j*2+1]);
|
Chris@19
|
332 }
|
Chris@19
|
333 // FFTW returns only the first half of the output (plus
|
Chris@19
|
334 // DC/Nyquist) -- synthesise the conjugate half
|
Chris@19
|
335 for (var j = 1; j < size/2; ++j) {
|
Chris@19
|
336 total += Math.sqrt(out[j*2] * out[j*2] + out[j*2+1] * out[j*2+1]);
|
Chris@19
|
337 }
|
Chris@19
|
338 }
|
Chris@19
|
339
|
Chris@19
|
340 var end = performance.now();
|
Chris@19
|
341
|
Chris@19
|
342 report("fftw", start, middle, end, total);
|
Chris@19
|
343
|
Chris@19
|
344 fft.dispose();
|
Chris@19
|
345 }
|
Chris@19
|
346
|
Chris@40
|
347 var sizes = [ 4, 8, 512, 2048 ];
|
Chris@37
|
348 var tests = [ testNayuki, testNayukiObj, testNayukiC, testNayukiCF,
|
Chris@37
|
349 testKissFFT, testKissFFTCC, testCross, testFFTW,
|
Chris@37
|
350 testNockert, testDntj ];
|
Chris@16
|
351 var nextTest = 0;
|
Chris@19
|
352 var nextSize = 0;
|
Chris@16
|
353 var interval;
|
Chris@16
|
354
|
Chris@3
|
355 function test() {
|
Chris@16
|
356 clearInterval(interval);
|
Chris@19
|
357 if (nextTest == tests.length) {
|
Chris@19
|
358 nextSize++;
|
Chris@19
|
359 nextTest = 0;
|
Chris@19
|
360 if (nextSize == sizes.length) {
|
Chris@19
|
361 return;
|
Chris@19
|
362 }
|
Chris@16
|
363 }
|
Chris@19
|
364 f = tests[nextTest];
|
Chris@19
|
365 size = sizes[nextSize];
|
Chris@19
|
366 nextTest++;
|
Chris@19
|
367 f(size);
|
Chris@19
|
368 interval = setInterval(test, 100);
|
Chris@16
|
369 }
|
Chris@3
|
370
|
Chris@16
|
371 window.onload = function() {
|
Chris@3
|
372 document.getElementById("test-description").innerHTML =
|
Chris@19
|
373 "Running " + 2*iterations + " iterations per implementation.<br>Timings are given separately for the first half of the run (" + iterations + " iterations) and the second half, in case the JS engine takes some warming up.<br>Each cell contains results for the following sizes: " + sizes;
|
Chris@19
|
374 interval = setInterval(test, 100);
|
Chris@3
|
375 }
|
Chris@3
|
376
|