- From: Marcus Geelnard <mage@opera.com>
- Date: Thu, 19 Jul 2012 21:03:38 +0000
- To: Jussi Kalliokoski <jussi.kalliokoski@gmail.com>
- Cc: public-audio@w3.org
Citerar Jussi Kalliokoski <jussi.kalliokoski@gmail.com>:
> Obviously SIMD code is faster than addition in JS now, for example. And
> yes, IIR filter is a type of a convolution, but I don't think it's possible
> to write an efficient IIR filter algorithm using a convolution engine --
> after all, a convolution engine should be designed to deal with a FIRs.
You can split the IIR filter into two parts: an FIR part and a
recursive part. If you skip the recursive part, you get plain
convolution.
> Not to mention that common IIR filters have 4 (LP, HP, BP, N) kernels,
I was thinking more along the lines of using the more generic form of
the filter function (see the Matlab filter() function [1]), which
supports everything from first order IIR filters to FIR-filters with
1000s of taps (or more, obviously).
> And as far as IIR filter performance goes, I think SIMD
> instructions offer very little usefulness in IIR algorithms, since they're
> so linear.
Actually, I made a simple benchmark on my AMD x2, 64-bit Linux, using
an IIR filter function identical to the filter function in the
BiquadFilterNode (excluding buffer edge handling).
JavaScript code:
function filter (y, x, b, a) {
var b0 = b[0], b1 = b[1], b2 = b[2];
var a1 = a[0], a2 = a[2];
var len = x.length;
for (var k = 2; k < len; ++k)
y[k] = b0 * x[k] + b1 * x[k-1] + b2 * x[k-2] - a1 * y[k-1] - a2 * y[k-2];
}
C++ code:
void filter(float* y, const float* x, unsigned len, const float* b,
const float* a)
{
float b0 = b[0], b1 = b[1], b2 = b[2];
float a1 = a[0], a2 = a[2];
// FIR part
for (unsigned k = 2; k < len; ++k)
y[k] = b0 * x[k] + b1 * x[k-1] + b2 * x[k-2];
// Recursive part
for (unsigned k = 2; k < len; ++k)
y[k] -= a1 * y[k-1] + a2 * y[k-2];
}
Note: In the C++ version I split the FIR and recursive parts into two
separate loops because somehow g++ managed to optimize that better
than doing a single loop (looking at the assembler output, it actually
SIMD:ified the FIR part for me - impressive!). Also, it illustrates
that you can do the FIR part first, with optimizations (e.g. FFT-based
convolution or SIMD), and then append the recursive part as a second
pass.
Benchmark results (using 128 samples long buffers):
C++: 529.4 Ms/s
Chrome: 22.0 Ms/s
FF: 13.0 Ms/s
Opera: 9.0 Ms/s
So, we actually get a >20x speedup when going from the fastest JS
engine to native C++ code (g++ -O3 -ffast-math). I think that's
motivation enough to implement it as a native function ;)
Regards,
Marcus
[1] http://www.mathworks.se/help/techdoc/ref/filter.html
--
Marcus Geelnard
Core Graphics Developer
Opera Software ASA
Received on Thursday, 19 July 2012 21:12:23 UTC