Предложения по оптимизации этого Java-кода с помощью математических функций

1

У меня есть следующий код Java. Я пытаюсь оптимизировать функцию

        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;


    }

Сроки по файлам трассировки:

Всего 2500 мс fft ~ 500 мс self ~ 900 мс секунд для цикла ~ 900 мс

Итак, я сосредоточен на том, чтобы оптимизировать второй цикл while. Я не могу изменить функцию fft.

По тому же вопросу я не уверен, почему трассировщик сообщает "я", чтобы он составлял 900 мс.

Теги:

3 ответа

2
Лучший ответ

Исходный код можно упростить, применяя математические свойства функции журнала.

Рассмотрим следующую функцию, извлеченную из исходного кода:

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 ) );
}

В принципе, в псевдокоде есть следующая функция:

x = sqrt(w[2i]^2 + w[2i+1]^2)
return 20 * log( abs(x) / ( windowEnd - pStart ) )
  • abs не требуется, потому что значение sqrt() неотрицательно.
  • log (X/Y) == log (X) - log (Y)
  • log (sqrt (X)) = 0,5 log (X)
  • log (windowEnd - pStart) может быть предварительно рассчитан и обналичен

Упрощенный вариант с объяснениями каждого шага:

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;
}
  • 0
    Хотя, новый подход, но с этим, я заканчиваю тем, что использовал два вызова Math.log10 - которые дороги. Я проверил это, и мое время выполнения фактически увеличилось. Я удалил функцию пресса.
  • 0
    Я уже дважды упоминал, что вы можете кэшировать Math.log10 (windowEnd - pStart) вне этой функции и вне вашего внутреннего цикла. Если все сделано правильно, у вашего внутреннего цикла будет один вызов Math.log10 и ноль делений.
Показать ещё 2 комментария
3

Ваш код является легкой целью для распараллеливания. Вы можете:

  1. сделайте это вручную, вычисляя индексы подмашины, чтобы перейти к каждой нити;
  2. используйте ForkJoin который обрабатывает многие аспекты для вас;
  3. используйте только что выпущенную Java 8 и напишите ее как задачу обработки parallelStream потока.

Мой выбор, безусловно, будет номером 3, если не более того для удовольствия.

Я потратил некоторое время, чтобы измерить ваш код в моей настройке, используя jmh. Это занимает 14 наносекунд за вход в window массив. Учитывая объем выполненных расчетов, я думаю, что это уже отличный результат и не может быть улучшен с какой-либо значительной разницей.

  • 0
    Я забыл, что было сегодня - хороший звонок!
  • 0
    Спасибо, я посмотрел на java8. Даст вариант 3 попробовать. Я отредактировал пост, чтобы включить полный код и время
0

Простые оптимизации, которые вы можете сделать, - это сохранить значение window[i*2] и window[i*2+1] в переменной и использовать их в методе sqrt() что уменьшит количество обращений к массиву.

Кроме этого, если бы вы могли сказать, что вы пытаетесь сделать, мы могли бы помочь вам лучше.

  • 0
    JIT будет обрабатывать ограниченный доступ к массиву в любом случае.
  • 0
    Ох ... я этого не знал .. спасибо

Ещё вопросы

Сообщество Overcoder
Наверх
Меню