6
\$\begingroup\$

I wrote a speech enhancement code for an Android App. The algorithm runs on 256 size frames of voice samples. On my PC the code runs per about 5ms per frame, while on my Nexsus 5 it more like 50ms per frame, making the speech enhancement on a 30sec long recording run for over two minutes. On my PC, enhancing the whole audio file takes under a minute. I am looking to improve run time anywhere I can. I am attaching the code that runs per frame which is where I see biggest increase in run time.

for(int j = 0; j < Consts.NUM_OF_CLUSTERS; j++)
{
    tmp_log_h=0;
    for(int k = 0; k < Consts.SIZE_OF_SEMPELS_IN_CHUNCK; k++)
    {

        f_X = (1/FastMath.sqrt(2*FastMath.PI*sigmaSqr.get(j).get(k)))*FastMath.exp(-(FastMath.pow(NoisySignal[k] - mue.get(j).get(k),2))/(2*sigmaSqr.get(j).get(k)));// equation (3)
        F_X=(0.500 + 0.5*Erf.erf((NoisySignal[k] - mue.get(j).get(k))/(FastMath.sqrt(2*sigmaSqr.get(j).get(k)))));// equation (6)

        if(j==0)
        {
            g_pdf_Y[k]=1/FastMath.sqrt(2*FastMath.PI*Noise_sigmaSqr.get(k))*FastMath.exp(-(FastMath.pow(NoisySignal[k] - Noise_mu.get(k),2))/(2*Noise_sigmaSqr.get(k)));// equation (4)
            G_cdf_Y[k]=(0.500 + 0.5*Erf.erf((NoisySignal[k] - Noise_mu.get(k))/(FastMath.sqrt(2*Noise_sigmaSqr.get(k)))));// equation (5)
        }

        tmp_R_samples = (f_X/F_X);// equation (12.5)

        if(j==0)
        {
            R_noise[k]=g_pdf_Y[k]/G_cdf_Y[k]; // equation (12.5)
        }

        Roe[j][k] = 1/(1 + (R_noise[k]/tmp_R_samples));
        X_hat[j][k] = NoisySignal[k]*Roe[j][k]+ (mue.get(j).get(k))*(1 - Roe[j][k]) - (sigmaSqr.get(j).get(k))*((f_X*R_noise[k])/(f_X + F_X*R_noise[k]));
        tmp_h_first=(f_X*G_cdf_Y[k] + F_X*g_pdf_Y[k]);// equation (7.5)             
        tmp_log_h += FastMath.log(tmp_h_first); // equation (7.5)
    }


    Log_h_second[j]=tmp_log_h;
    Log_h_final_vecotr[j]=FastMath.log(c.get(j)) + Log_h_second[j];
}


Double maxVi = MathHelpers.findMax(Log_h_final_vecotr);

double sumExp = 0.0;

for(int j = 0; j < Consts.NUM_OF_CLUSTERS; j++)
{
    sumExp +=(FastMath.exp(Log_h_final_vecotr[j] - maxVi));// equation (18)
    System.out.println(sumExp);
}

Log_h_final = maxVi + FastMath.log(sumExp);


for(int j = 0; j < Consts.NUM_OF_CLUSTERS; j++)
{
    Log_q[j]=FastMath.log(c.get(j)) + Log_h_second[j] - Log_h_final;
    q[j]=(FastMath.exp(Log_q[j]));// equation (10)
}


for(int k = 0; k < Consts.SIZE_OF_SEMPELS_IN_CHUNCK; k++)
{
    Double temp_X_estimate = 0.0;

    for(int j = 0; j < Consts.NUM_OF_CLUSTERS; j++)
    {
        temp_X_estimate += (X_hat[j][k]*q[j]);
    }   

    X_estimate[k]=temp_X_estimate;// equation (9)
}
\$\endgroup\$
5
\$\begingroup\$

Performance

I cannot offer any mayor improvements, just some small things:

You should save results of a calculation or action instead of doing them over and over.

For example:

  • 2*FastMath.PI: this is not dependent on the loop variables, just save it in a variable (same goes for 2*sigmaSqr.get(j).get(k) which you are doing three times for every loop; and NoisySignal[k] - mue.get(j).get(k) which you are doing twice). These duplicate calculations are quite hard to catch in your code. If you write the equations on paper, it becomes easier to spot.
  • mue.get(j).get(k): you are doing this three times. I don't know what mue is, but depending on the kind of list, it may be expensive (same goes for sigmaSqr.get(j).get(k))

You could also move the calculation of X_estimate inside the previous j loop:

for(int j = 0; j < Consts.NUM_OF_CLUSTERS; j++)
{
    Log_q[j]=FastMath.log(c.get(j)) + Log_h_second[j] - Log_h_final;
    q[j]=(FastMath.exp(Log_q[j]));// equation (10)

    Double temp_X_estimate = 0.0;
    for(int k = 0; k < Consts.SIZE_OF_SEMPELS_IN_CHUNCK; k++)
    {       
        temp_X_estimate += (X_hat[j][k]*q[j]);        
    }
    X_estimate[k]=temp_X_estimate;// equation (9)
}

It will save you running through the loop once, you can profile it to see if it is worth the loss in readability.

Readability

Code Structure

I would probably pull out the j==0 case in front of the loop. Keeping it in the loop is confusing and seems unnecessary.

Function calls do cause an overhead, but I would still consider extracting some of the code to separate functions (this would make profiling the code easier).

Additional variables also cause a slight overhead, but some of your expressions are quite long and hard to grasp. You might consider adding intermediate variables with an expressive name saving a temporary state of the calculation. An alternative would be to use newlines to present the expressions in a better way.

Variable Names

I don't think that your variable names are very expressive. I'm not that deep into the topic, but I would guess that at least some of them can be improved upon.

Also, just using case to differentiate names is quite confusing (f_X vs F_X).

Comments

I'm guessing that you have some kind of documentation to which your current comments refer. But without it, they are not that useful. It might be helpful to add this documentation (or a short excerpt) to the top.

Style

Please follow general style guidelines.

  • variable names start with a lowercase letter (this lets a reader easily see that it is a variable, not a class)
  • declare variables where they are used (tmp_log_h for example is declared outside the loop, even though it is not needed there)
  • use more spaces (for example, before and after ==, =, /, *, etc)
  • in Java, camelCase is usually used instead of underscores (you do this sometimes (eg maxVi or sumExp, but mostly you use underscores)

Misc

  • get rid of unnecessary brackets (for example (X_hat[j][k]*q[j]), tmp_R_samples = (f_X/F_X), or f_X = (...); especially in the last example, the expression already has enough parentheses without the extra once :) )
\$\endgroup\$
3
\$\begingroup\$

Performance improvements

  1. Move the if (j==0) { ... } cases to outside of the nested loops. They are checked in every iteration of your nested loops.

  2. Review the code carefully, and look for any steps that are unnecessarily repeated. Extract these operations to temporary local variables, so that you calculate their values only once, only when necessary, and reuse, often multiple times.

  3. Change Double temp_X_estimate to double temp_X_estimate. In every iteration you are adding to it with temp_X_estimate += (X_hat[j][k]*q[j]), and I strongly suspect that the right-hand side is a double. This is inefficient, because there will be an unnecessary unboxing and autoboxing here: temp_X_estimate will be unboxed to perform the += calculation, and then it will be boxed back to Double.

When every bit of CPU counts, these measures together should make a noticeable difference.

Coding style

You're violating formatting, indentation and naming standards. Any descent IDE would reformat your code nicely, and should also warn about naming violations. Your code as it is, is extremely hard to read, and why should the reviewer care if you don't?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.