после профилирования много я узнал, что этот метод поднимает большую часть % времени вычисления. Я действительно не вижу способ оптимизировать, так как это - ужасная функция. (это...), Возможно, кто-то может показать мне приблизительно некоторую хорошую идею?
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%, убыстритесь:
Спасибо до сих пор!
Edit2: Я не знаю который ответ принять.:( С некоторыми другими улучшениями (главным образом справочные таблицы) время обработки для 9 000 звуковых файлов перешло по сравнению с 4:30 минуты к 3:28 минуты.
Я сохраню этот вопрос открытым, чтобы видеть, существуют ли другие идеи, но затем принимают один ответ.
Править: Я отчасти расстроен теперь. Я использую JFace treeviewer, чтобы позволить пользователю просмотреть результаты, и требуется больше времени для обновления, чем вычисления самого.:/
Ваша функция кажется аналитической, я бы предложил полностью заменить ее методом интерполяции. Таким образом, вы сокращаете дорогостоящие вызовы 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.
Глядя на этот документ, на который вы ссылаетесь, кажется, что L_ETQ и a0 - это просто функция частоты (лай) звука.
Итак, по крайней мере, вы можете составить таблицу результатов различных расчетов для заданных частот. Например, кэшируйте результаты:
.064 * Math.pow(10, 0.025 * L_ETQ)
по частоте. [Может также кешировать (a0 + L_ETQ) * .1]
Также, вероятно, незначительный эффект, если он есть, но я бы преобразовал 1/4 в 0,25.
Математика не сразу выглядит так, как будто ее можно переупорядочить, чтобы избежать дублирования вычислений, поэтому подход зависит от того, как используется эта функция и насколько точные результаты вам нужны.
Лучшим подходом было бы избегать повторного вычисления значения для одного и того же набора входных значений. Может ли ваш код сохранять результаты вычислений для одних и тех же входных значений? Если нет, то вы можете иметь кэш для значений, но будьте осторожны, поскольку двойные значения могут иметь очень много значений, вы можете захотеть сложить двойные значения в известный интервал (например, от 0 до 1 складывается в целые числа от 0 до 99).
Может помочь кэширование выходных данных по входным параметрам:
Об этом еще не упоминали, так что сделаю.
Вы можете подумать о переходе от математики с плавающей запятой к целым числам. Операции выполняются немного быстрее. Графика, как правило, использует целочисленную математику, а не плавающую, из-за того, как добавляются и сохраняются числа с плавающей запятой. Вам придется конвертировать в и из, но я уверен, что вы получите значительный прирост производительности. Единственная проблема с целочисленной математикой заключается в том, что вы должны определить, с какой точностью вы готовы жить.
Предварительно сгенерируйте таблицу поиска для диапазона входных данных, который может обрабатывать ваша программа.
Быстрее этого не бывает! :)
Думаю,
double t2 = Math.sqrt(Math.sqrt(t1));
быстрее, чем
double t2 = Math.pow(t1, 0.25);