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

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

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% с 7 000 000 вызовов
  • Новый: 3,2% 8 000 000 вызовов.

Спасибо до сих пор!

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

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

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

17
задан Jim Ferrans 26 July 2010 в 13:26
поделиться

8 ответов

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

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

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

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

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

Math.pow(10, 0.025 * L_ETQ); 

на

Math.exp(L_ETQ * 0.05756462732485114210d)

(что составляет exp (L_ETQ * 0,025 * log (10)) ).

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

РЕДАКТИРОВАТЬ: См. График t2 как функцию L_G - a0 - L_ETQ .

РЕДАКТИРОВАТЬ: Замените

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.

23
ответ дан 30 November 2019 в 12:06
поделиться

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

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

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

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

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

2
ответ дан 30 November 2019 в 12:06
поделиться

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

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

3
ответ дан 30 November 2019 в 12:06
поделиться

Может помочь кэширование выходных данных по входным параметрам:

http://en.wikipedia.org/wiki/Memoization

1
ответ дан 30 November 2019 в 12:06
поделиться
  1. попытаться кэшировать некоторые значения (я предполагаю, что L_G и L_ETQ не такие переменные, верно?)
  2. попытаться реализовать в архитектурно-зависимом коде и использовать JNI.
0
ответ дан 30 November 2019 в 12:06
поделиться

Об этом еще не упоминали, так что сделаю.

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

1
ответ дан 30 November 2019 в 12:06
поделиться

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

Быстрее этого не бывает! :)

1
ответ дан 30 November 2019 в 12:06
поделиться

Думаю,

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

быстрее, чем

double t2 = Math.pow(t1, 0.25);
3
ответ дан 30 November 2019 в 12:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: