代码之家  ›  专栏  ›  技术社区  ›  InsertNickHere

是否可以优化此功能?

  •  17
  • InsertNickHere  · 技术社区  · 14 年前

    在分析了很多之后,我发现这个方法占用了大部分的计算时间。我真的看不到优化的方法,因为它是一个可怕的函数。(是……) 也许有人能给我一些好主意?

    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);
     }
    

    对ltqfactor的查找是这样进行的。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%,70万次通话
    • 新电话:3.2%8000000次。

    到目前为止谢谢你!

    伊迪丝2:我不知道该接受哪个答案。:( 随着其他一些改进(主要是查找表),9000个声音文件的处理时间从4:30分钟下降到3:28分钟。

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

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

    9 回复  |  直到 11 年前
        1
  •  23
  •   BenMorel Manish Pradhan    11 年前

    你的函数似乎是解析函数,我建议用插值法完全替换它。这样,您就可以将昂贵的呼叫减少到 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)) )

    所以你可以用一些算术运算和一个指数运算。

    编辑: 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。

        2
  •  3
  •   Thorbjørn Ravn Andersen    14 年前

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

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

        3
  •  3
  •   Landei    14 年前

    我猜

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

    比快

    double t2 = Math.pow(t1, 0.25);
    
        4
  •  2
  •   aepryus    14 年前

    看一眼你提到的那篇论文,似乎l_etq和a0只是声音频率(树皮)的函数。

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

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

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

    还有,可能是轻微的影响,如果有的话,但我会将1/4转换为0.25。

        5
  •  1
  •   Fredrick Pennachi    14 年前

    为程序可以处理的输入范围预先生成一个查找表。

    再快不过了!:)

        6
  •  1
  •   Dave Bish    14 年前

    根据输入参数缓存输出可能有助于:

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

        7
  •  1
  •   monksy    14 年前

    这还没有被提及,所以我会的。

    您可能需要考虑从浮点数学移动到整数。操作要快一点。由于浮点数是如何添加和存储的,图形倾向于使用整数数学而不是浮点。你将不得不转换为和来自,但我相信你会得到相当大的性能提升。整数数学唯一的问题是你必须定义你愿意接受的精度。

        8
  •  0
  •   onof    14 年前
    1. 尝试缓存一些值(我猜l_g和l_etq不是那个变量,对吗?)
    2. 尝试在依赖架构的代码中实现并使用JNI。
        9
  •  0
  •   Community CDub    8 年前

    我愿意 take some stackshots against it 以消除猜测。 这样我就可以确定时间不会花在其他地方,比如读取数据并将其转换为浮点,或者打开/关闭文件。 当我确定这个程序花费了大部分时间时,我很确定它将花费几乎所有的时间来调用数学函数,以及将l_etq转换为整数。也许可以记忆这些数学函数。然后,正如亚历山德拉所说,你也许可以用插值来完成这一切。

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