是否可以优化此功能?

时间:2010-07-23 08:55:00

标签: java math function optimization

经过剖析后,我发现这种方法占用了大部分计算时间。我真的没有看到优化的方法,因为它是一个可怕的功能。 (它是...) 也许有人能给我一些好主意吗?

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从给定的ltq函数中保持20点,大约应该是足够的。

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:30min降至3:28min。

我会保持这个问题,看看是否有其他想法,但接受一个答案。

编辑:我现在有点沮丧。我使用JFace treeviewer让用户浏览结果,它需要更多的时间来更新计算本身。 :/

9 个答案:

答案 0 :(得分:23)

你的功能似乎是分析的,我建议用插值方法完全替换它。这样,您就可以将对Math.Pow的昂贵调用减少到一些算术运算。

这种情况下最好的应该是有理函数逼近。你的函数可能在复平面上有极点,这通常会使多项式插值失败。

请注意,您有两个变量:L_G - a0 - L_ETQL_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)))。

所以你应该对一些算术运算和指数运算很好。

编辑: See a graph of t2 as a function of 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。

答案 1 :(得分:3)

数学并不会立即看起来像是可以重新排序以避免任何重复计算,因此采用的方法取决于此函数的使用方式以及所需的准确结果。

最好的方法是避免重新计算同一组输入值的值。您的代码可以保存相同输入值的计算结果吗?如果没有,你可以有一个值的缓存,但要注意双打可以有很多值,你可能想要将双打折叠成一个已知的间隔(例如从0到1折叠成0到99之间的整数)。

答案 2 :(得分:3)

我猜是

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

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

答案 3 :(得分:2)

在浏览你引用的那篇论文时,似乎L_ETQ和a0只是声音频率(Bark)的函数。

所以,至少你可以想出一个给定频率的各种计算结果表。例如,缓存以下结果:

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

按频率。 [也可以缓存(a0 + L_ETQ)* .1]

此外,可能是次要影响,如果有的话,但我会将1/4转换为0.25。

答案 4 :(得分:1)

答案 5 :(得分:1)

答案 6 :(得分:1)

答案 7 :(得分:0)

  1. 尝试缓存一些值(我猜L_G和L_ETQ不是那个变量,对吗?)
  2. 尝试在体系结构相关代码中实现并使用JNI。

答案 8 :(得分:0)

我会take some stackshots against it,以消除猜测。 这样我就可以确定在其他地方没有采取时间,比如读取数据并将其转换为浮点数,或打开/关闭文件。 当我确定这个例程占用了大部分时间时,我很确定它几乎会花费所有时间来调用Math函数,并将L_ETQ转换为整数。有可能记住那些数学函数。然后,正如亚历山大所说,你可以通过插值来完成所有这些。

问题是,你可能有不止一件事要优化。如果这需要180秒,如果50%,比如那个时间,这个例程在堆栈上,那么如果你将时间缩短一半,你将时间减少了45秒到135秒。但是,现在这个例程只在堆栈上持续45/135秒或1/3。这意味着其他一些东西正在使用其他2/3或90秒,我敢打赌,你可以优化一些东西。如果你可以将那些减少到45秒,那么总数减少到90,并且数学例程所占的百分比回升到50%,所以也许你可以从中挤出更多。就是这样。每当你减少其中的一部分时,其他部分的百分比会增加,所以你可以一遍又一遍地追踪它们,直到你真的尽可能地挤压它。