Friday, February 6, 2009

Damn You, GSL!

One part of this program I wrote is a calculator of respective loudness of the keys of, as a default and in the context of this program, a standard 88-key piano. It does this by maintaining a matrix of Gaussian normal curves in memory, which look something like this:

A representation of just the middle C octave. Note that in the actual program, the full spectrum of octaves is represented.

Which it then multiplies by a 1-row matrix of the incoming amplitudes of the audio samples' frequency bin values from Math::FFTW.

This, in essence, works by taking the Y values of each key's curve and multiplying it the corresponding values in the FFT deconstruction, then summing the results.

Anyway, I'll get off of this digression and back to the problem at hand.

Any way you slice it, this whole process involves a LOT of matrix math between HUGE matrices, including constructing a lot of new matrices for a second, then multiplying them and returning the result.

At first I chose a simple and easy-to-use matrix math library, Math::MatrixReal. This was a little slow to begin with, plus I had to code in a workaround for creating the aforementioned huge matrices, but it worked... But it was S-L-O-W.

I decided to switch to a C library that would hopefully get the job done faster. I found, much to my delight, Math::GSL, which had a specific library for dealing with matrices at a very low level. It even had an extensive basic linear algebra system (BLAS) to do exactly what I had in mind, not to mention that it was the motherfucking Gnu Scientific Library I would be playing with. Yay!

After I switched, I noticed that when the program was processing audio samples, instead of using ~200 megs of memory, it was now using ~70 megs of memory.

All was not right, though. Now I was having a problem with my program. As it got further and further into processing the file, I noticed it took up more and more memory, eventually ballooning out to 2 gigs of memory once. This especially happened when I was processing multiple files at once.

Long story short, I figured out that it was the GSL matrices that were staying latent in memory long after the program's Math::GSL::Matrix objects were destroyed. Luckily I found a way to solve it:

use Math::GSL::Matrix qw/ :all /;

my $matrix = Math::GSL::Matrix->new($rows, $cols);

# ...

# clean up!
&gsl_matrix_free($matrix->raw);

Now that I solved this problem, it should purr like a kitten.

No comments: