Подтвердить что ты не робот

Можно ли оптимизировать эту функцию?

после профилирования много я выяснил, что этот метод занимает большую часть% времени вычисления. Я не вижу способа оптимизировать, так как это ужасная функция. (это...) Может быть, кто-то может показать мне какую-нибудь приятную идею?

public static double perceivedLoudness(double L_G, double L_ETQ, double a0) {
  double t1 = 1d + 1 / 4d * Math.pow(10d, 0.1d * (L_G - a0 - L_ETQ));
  double t2 = Math.pow(t1, 0.25);
  return 0.064d * Math.pow(10, 0.025 * L_ETQ) * (t2 - 1);
 }

Вот улучшенная версия:

public static double perceivedLoudness(double L_G, double L_ETQ, double a0) {
  double x = L_G - a0 - L_ETQ;
  double t1 = 0.25 * Math.exp(0.230259 * x) + 1;
  double t2 = Math.sqrt(Math.sqrt(t1));
  return ltqFactors[(int)L_ETQ]  * (t2 - 1);
 }

Поиск ltqFactors идет таким образом. ltqValues ​​удерживают 20 точек от заданной функции ltq, что приблизительно должно быть достаточным.

for( int i = 0; i < etqValues.length; ++i) {
  ltqFactors[(int)etqValues[i]] = 0.064d * Math.exp(etqValues[i] * 0.05756462732485114210d);
  }

Изменить: после того, как больше тестовых прогонов с большим количеством файлов, я набираю скорость до 100%:

  • Старый: 6,2% с 7000000 вызовами
  • Новое: 3,2% 8000000 звонков.

Спасибо, что до сих пор!

Edit2: Я не знаю, какой ответ принять.:( С некоторыми другими улучшениями (в основном, таблицы поиска) время обработки для 9000 звуковых файлов уменьшилось с 4: 30 мин до 3: 28мин.

Я буду держать этот вопрос открытым, чтобы увидеть, есть ли другие идеи, но затем принять один ответ.

Редактировать: Я сейчас расстроен. Я использую TreeViewer JFace, чтобы позволить пользователю просматривать результаты, и для обновления требуется больше времени, чем сами вычисления.:/

4b9b3361

Ответ 1

Ваша функция кажется аналитической, я бы предложил заменить ее полностью методом интерполяции. Таким образом, вы уменьшите дорогие вызовы до Math.Pow до нескольких арифметических операций.

Лучшее в этом случае должно быть приближением рациональной функции. Вероятно, ваша функция имеет полюсы в сложной плоскости, обычно это побеждает полиномиальную интерполяцию.

Обратите внимание, что у вас есть две переменные: L_G - a0 - L_ETQ и L_ETQ. Интерполяция должна выполняться только с одной переменной.

Я пошел бы на рациональную аппроксимацию функции t2 как функцию L_G - a0 - L_ETQ. Взгляните на Numerical Recipes для методов внедрения.

Кроме того, для последней части замените

Math.pow(10, 0.025 * L_ETQ); 

по

Math.exp(L_ETQ * 0.05756462732485114210d)

(что exp(L_ETQ * 0.025 * log(10))).

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

EDIT: Смотрите график t2 как функцию L_G - a0 - L_ETQ.

EDIT: Заменить

double t1 = 1d + 1 / 4d * Math.pow(10d, 0.1d * (L_G - a0 - L_ETQ)); 
double t2 = Math.pow(t1, 0.25);

по

double x = L_G - a0 - L_ETQ;
double t1 = 0.25 * Math.exp(0.230259 * x) + 1;
double t2 = Math.sqrt(Math.sqrt(t1));

и вы должны получить еще несколько%. На данный момент рациональная аппроксимация может быть чрезмерной: у вас есть два exp и два sqrt.

Ответ 2

Математика не выглядит так, как будто ее можно переупорядочить, чтобы избежать дублирования вычислений, поэтому подход к использованию зависит от того, как эта функция используется и как точные результаты вам нужны.

Лучшим подходом было бы избежать пересчета значения для того же набора входных значений. Может ли ваш код сохранять результаты расчета для тех же входных значений? Если нет, вы можете иметь кеш для значений, но будьте осторожны, чтобы удваивать может быть очень много значений, вам может понадобиться свернуть двойники на известный интервал (например, от 0 до 1 раза в целые числа от 0 до 99).

Ответ 3

Я бы предположил

double t2 = Math.sqrt(Math.sqrt(t1));

быстрее, чем

double t2 = Math.pow(t1, 0.25);

Ответ 4

Взглянув на эту бумагу, вы ссылаетесь, кажется, что L_ETQ и a0 - это просто функция частоты (Bark) звука.

Итак, по крайней мере, вы могли бы придумать таблицу результатов различных расчетов для заданных частот. Например, кешируйте результаты:

.064 * Math.pow(10, 0.025 * L_ETQ)

по частоте. [Может также кэшировать (a0 + L_ETQ) *.1]

Кроме того, возможно, незначительный эффект, если он есть, но я бы преобразовал 1/4 в 0,25.

Ответ 5

Предварительно создайте таблицу поиска для диапазона входов, которые может обрабатывать ваша программа.

Это не получается быстрее!:)

Ответ 7

Это еще не упоминается, так что я буду.

Вам может потребоваться перейти от математики с плавающей запятой к целому. Операции немного быстрее. Графика имеет тенденцию использовать целочисленную математику, а не плавать из-за того, как добавляются и сохраняются поплавки. Вам придется конвертировать в и из, но я уверен, что вы получите довольно мощный прирост производительности. Единственная проблема с целочисленной математикой заключается в том, что вам нужно определить, с какой степенью точности вы готовы жить.

Ответ 8

  • попытайтесь кэшировать некоторые значения (я думаю, L_G и L_ETQ не являются этой переменной, верно?)
  • попытайтесь реализовать код, зависящий от архитектуры, и использовать JNI.

Ответ 9

Я бы взял несколько стеков против него, чтобы устранить догадки. Таким образом, я мог быть уверен, что время не принимается в другом месте, например, при чтении данных и преобразовании их в плавающие точки или в файлах открытия/закрытия. Когда я был уверен, что эта процедура занимает большую часть времени, я уверен, что она будет тратить почти все время на вызовы математическим функциям и преобразовывать L_ETQ в целое. Возможно, можно запомнить эти математические функции. Затем, как сказал Александр, вы могли бы сделать все это с интерполяцией.

Дело в том, что у вас, вероятно, есть несколько вещей для оптимизации. Если это займет 180 секунд, а если 50%, скажем, того времени, что эта процедура находится в стеке, то, если вы сокращаете свое время наполовину, вы сокращаете время на 45 секунд до 135 секунд. Однако теперь эта процедура выполняется только в стеке на 45/135 секунд или 1/3. Это означает, что некоторые другие вещи используют другие 2/3 или 90 секунд, и я уверен, что есть некоторые вещи, которые вы могли бы оптимизировать. Если вы можете сократить их до, скажем, 45 секунд, тогда общая сумма будет равна 90, а%, принятая математической рутиной, вернется к 50%, так что, возможно, вы сможете выжать еще больше. Это как. Каждый раз, когда вы сокращаете одну часть, другие части увеличиваются в процентах, поэтому вы можете идти за ними снова и снова, пока вы действительно не сжали его как можно больше.