Himz
Himz

Reputation: 523

Suggestions for optimizing this java code with Math functions

I have the following java code. I am trying to optimize the function

        while(pStart < audio.length) {

        int pEnd = Math.round(pStart + winSize*Fs);

        int windowEnd = Math.min(pEnd, audio.length);
        double[] window = new double[fftSize*2];

        for(int i = pStart; i < windowEnd; i++) {
            window[(i-pStart)*2] = audio[i];
        }

        fft.complexForward(window);

        double fftVal;
        for(int i = 0; i < fftSize/2; i++) {

            fftVal = Math.sqrt((window[i*2] * window[i*2])  + (window[i*2+1] * window[i*2+1] ));

            powerAll[i][index] = 20 * Math.log10(
                    Math.abs(fftVal) / (windowEnd - pStart));
        }
        index++;


        pStart = pStart + windowSlide;


    }

Timing as per trace files:

Total 2500 ms fft ~500 ms self ~900 ms second for loop ~900 ms

So, my focus is to optimize the second for loop as for now. I cannot change the fft function.

On the same issue, I am not sure, why the tracer reports "self" to be 900 ms.

Upvotes: 1

Views: 109

Answers (3)

Oleg Estekhin
Oleg Estekhin

Reputation: 8395

The original code can be simplified by applying math properties of log function.

Consider the following function extracted from the original code:

double original( double[] window, int i, int windowEnd, int pStart ) {
    double fftVal = Math.sqrt(
            ( window[ i * 2 ] * window[ i * 2 ] )
                    + ( window[ i * 2 + 1 ] * window[ i * 2 + 1 ] )
    );
    return 20 * Math.log10(
            Math.abs( fftVal ) / ( windowEnd - pStart ) );
}

Basically, we have the following function in the pseudo-code:

x = sqrt(w[2i]^2 + w[2i+1]^2)
return 20 * log( abs(x) / ( windowEnd - pStart ) )
  • abs is not required, because the value of the sqrt() is non-negative.
  • log(X/Y) == log(X) - log(Y)
  • log(sqrt(X)) = 0.5 log(X)
  • log(windowEnd - pStart) can be precalculated and cashed

The simplified variant with explanations of each step:

double variant( double[] window, int i, int windowEnd, int pStart ) {

    // w[2i]^2 + w[2i+1]^2
    double temp1 = window[ i * 2 ] * window[ i * 2 ]
            + window[ i * 2 + 1 ] * window[ i * 2 + 1 ];

    // apply log(sqrt(X)) == log(X^0.5) == 0.5 log(X)
    double temp2 = 0.5 * Math.log10( temp1 );

    // calculate the value of Math.log10( windowEnd - pStart )
    // (and cache it outside of the function) 
    double tempConst3 = Math.log10( windowEnd - pStart );

    // apply log(X/Y) == log(X) - log(Y)
    double temp4 = temp2 - tempConst3;

    return 20 * temp4;
}

Upvotes: 2

Marko Topolnik
Marko Topolnik

Reputation: 200148

Your code is an easy target for parallelization. You could either:

  1. do it by hand, calculating the subarray indices to pass to each thread;
  2. use ForkJoin which handles many aspects for you;
  3. use Java 8 which has just been released and write this as a parallelStream processing task.

My choice would certainly be number 3, if nothing else then for the fun of it.

I took some time to measure your code on my setup, using jmh. It takes 14 nanoseconds per entry of the window array. Given the amount of calculation done I think this is already a great result and couldn't be improved by any significant margin.

Upvotes: 3

anirudh
anirudh

Reputation: 4176

The simple optimizations that you can do is to store the value of window[i*2] and window[i*2+1] in a variable and use them in the sqrt() method which will reduce the number of array accesses.

Other than that, if you could say what you are trying to do, we might be able to help you better.

Upvotes: 0

Related Questions