是否可以优化此功能?

2022-09-03 00:15:00

在分析了大量内容后,我发现此方法占用了大部分计算时间的百分比。我真的没有看到优化的方法,因为它是一个可怕的功能。(它是...)也许有人可以给我一些好主意左右?

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 的查找就是这样进行的。ltq 值从给定的 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 次呼叫。

谢谢到目前为止!

编辑2:我不知道该接受哪个答案。:(通过其他一些改进(主要是查找表),9000个声音文件的处理时间从4:30min减少到3:28min。

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

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


答案 1

您的函数似乎是分析性的,我建议用插值方法完全替换它。这样,您就可以将代价高昂的调用减少到几个算术操作。Math.Pow

在这种情况下,最好的应该是有理函数近似。您的函数很可能在复平面中具有极点,这通常会击败多项式插值。

请注意,您有两个变量:和 。插值应仅在一个变量中执行。L_G - a0 - L_ETQL_ETQ

我会选择的有理函数近似作为 的函数。查看数字配方,了解实现技术。t2L_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。


答案 2

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

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