【问题标题】:Java - Optimize function that contains mostly simple mathsJava - 优化包含大部分简单数学的函数
【发布时间】:2011-07-12 02:26:40
【问题描述】:

我应该尝试为我的团队优化此方法,使用 Java 开发视频解码器,但我没有看到任何好的方法。下面的函数似乎无法大幅加速,因为它主要包含简单的加法/减法/等。

void inverseTransform(int macroBlockIndex, int dataBlockIndex) {
    int[] workSpace = new int[64];
    short[] data = new short[64];

    int z1, z2, z3, z4, z5;
    int tmp0, tmp1, tmp2, tmp3;
    int tmp10, tmp11, tmp12, tmp13;

    int pointer = 0;

    for (int index = 8; index > 0; index--) {
        if (dataBlockBuffer[pointer + 8] == 0 && dataBlockBuffer[pointer + 16] == 0 && dataBlockBuffer[pointer + 24] == 0 && dataBlockBuffer[pointer + 32] == 0 && dataBlockBuffer[pointer + 40] == 0 && dataBlockBuffer[pointer + 48] == 0 && dataBlockBuffer[pointer + 56] == 0) {
            int dcValue = dataBlockBuffer[pointer] << PASS1_BITS;

            workSpace[pointer + 0] = dcValue;
            workSpace[pointer + 8] = dcValue;
            workSpace[pointer + 16] = dcValue;
            workSpace[pointer + 24] = dcValue;
            workSpace[pointer + 32] = dcValue;
            workSpace[pointer + 40] = dcValue;
            workSpace[pointer + 48] = dcValue;
            workSpace[pointer + 56] = dcValue;

            pointer++;
            continue;
        }

        z2 = dataBlockBuffer[pointer + 16];
        z3 = dataBlockBuffer[pointer + 48];

        z1 = (z2 + z3) * FIX_0_541196100;
        tmp2 = z1 + z3 * -FIX_1_847759065;
        tmp3 = z1 + z2 * FIX_0_765366865;

        z2 = dataBlockBuffer[pointer];
        z3 = dataBlockBuffer[pointer + 32];

        tmp0 = (z2 + z3) << BITS;
        tmp1 = (z2 - z3) << BITS;

        tmp10 = tmp0 + tmp3;
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        tmp0 = dataBlockBuffer[pointer + 56];
        tmp1 = dataBlockBuffer[pointer + 40];
        tmp2 = dataBlockBuffer[pointer + 24];
        tmp3 = dataBlockBuffer[pointer + 8];

        z1 = tmp0 + tmp3;
        z2 = tmp1 + tmp2;
        z3 = tmp0 + tmp2;
        z4 = tmp1 + tmp3;
        z5 = (z3 + z4) * FIX_1_175875602;

        tmp0 = tmp0 * FIX_0_298631336;
        tmp1 = tmp1 * FIX_2_053119869;
        tmp2 = tmp2 * FIX_3_072711026;
        tmp3 = tmp3 * FIX_1_501321110;
        z1 = z1 * -FIX_0_899976223;
        z2 = z2 * -FIX_2_562915447;
        z3 = z3 * -FIX_1_961570560;
        z4 = z4 * -FIX_0_390180644;

        z3 += z5;
        z4 += z5;

        tmp0 += z1 + z3;
        tmp1 += z2 + z4;
        tmp2 += z2 + z3;
        tmp3 += z1 + z4;

        workSpace[pointer + 0] = ((tmp10 + tmp3 + (1 << F1)) >> F2);
        workSpace[pointer + 56] = ((tmp10 - tmp3 + (1 << F1)) >> F2);
        workSpace[pointer + 8] = ((tmp11 + tmp2 + (1 << F1)) >> F2);
        workSpace[pointer + 48] = ((tmp11 - tmp2 + (1 << F1)) >> F2);
        workSpace[pointer + 16] = ((tmp12 + tmp1 + (1 << F1)) >> F2);
        workSpace[pointer + 40] = ((tmp12 - tmp1 + (1 << F1)) >> F2);
        workSpace[pointer + 24] = ((tmp13 + tmp0 + (1 << F1)) >> F2);
        workSpace[pointer + 32] = ((tmp13 - tmp0 + (1 << F1)) >> F2);

        pointer++;
    }

    pointer = 0;

    for (int index = 0; index < 8; index++) {
        z2 = workSpace[pointer + 2];
        z3 = workSpace[pointer + 6];

        z1 = (z2 + z3) * FIX_0_541196100;
        tmp2 = z1 + z3 * -FIX_1_847759065;
        tmp3 = z1 + z2 * FIX_0_765366865;

        tmp0 = (workSpace[pointer + 0] + workSpace[pointer + 4]) << BITS;
        tmp1 = (workSpace[pointer + 0] - workSpace[pointer + 4]) << BITS;

        tmp10 = tmp0 + tmp3;
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        tmp0 = workSpace[pointer + 7];
        tmp1 = workSpace[pointer + 5];
        tmp2 = workSpace[pointer + 3];
        tmp3 = workSpace[pointer + 1];

        z1 = tmp0 + tmp3;
        z2 = tmp1 + tmp2;
        z3 = tmp0 + tmp2;
        z4 = tmp1 + tmp3;

        z5 = (z3 + z4) * FIX_1_175875602;

        tmp0 = tmp0 * FIX_0_298631336;
        tmp1 = tmp1 * FIX_2_053119869;
        tmp2 = tmp2 * FIX_3_072711026;
        tmp3 = tmp3 * FIX_1_501321110;
        z1 = z1 * -FIX_0_899976223;
        z2 = z2 * -FIX_2_562915447;
        z3 = z3 * -FIX_1_961570560;
        z4 = z4 * -FIX_0_390180644;

        z3 += z5;
        z4 += z5;

        tmp0 += z1 + z3;
        tmp1 += z2 + z4;
        tmp2 += z2 + z3;
        tmp3 += z1 + z4;

        data[pointer + 0] = (short) ((tmp10 + tmp3) >> F3);
        data[pointer + 7] = (short) ((tmp10 - tmp3) >> F3);
        data[pointer + 1] = (short) ((tmp11 + tmp2) >> F3);
        data[pointer + 6] = (short) ((tmp11 - tmp2) >> F3);
        data[pointer + 2] = (short) ((tmp12 + tmp1) >> F3);
        data[pointer + 5] = (short) ((tmp12 - tmp1) >> F3);
        data[pointer + 3] = (short) ((tmp13 + tmp0) >> F3);
        data[pointer + 4] = (short) ((tmp13 - tmp0) >> F3);

        pointer += 8;
    }
    short[] temp = imageSlice.MacroBlocks[macroBlockIndex].DataBlocks[dataBlockIndex];
    for (int i = 0; i < data.length; i++)
        temp[i] = data[i]; //imageSlice.MacroBlocks[macroBlockIndex].DataBlocks[dataBlockIndex][i] = data[i];
}

如果可以的话,我应该结合基础数学吗?或者你有什么建议?

【问题讨论】:

  • 大多数人会建议:找到性能下降的地方,创建一个较小的问题来显示并使用该信息重新提出问题
  • 嗯,两个 for 循环花费的时间几乎相同,因此整个方法的性能下降。我本可以将显示拆分为每个单独的 for 循环,但它会在两个单独的帖子中。
  • 用汇编程序编写。不过,严肃地说,我没有看到任何快捷方式......但是你完全无法描述的变量名对我来说也没有任何意义,所以我并没有花大量时间试图弄清楚它是怎么回事做它正在做的事情。
  • @MirroredFate 我明白了。我也与变量作斗争,但其中许多仅用于临时存储值并被重用。因此,变量实际上没有描述性名称。
  • 是的,检查更高级别的优化通常是个好主意,但是对于“指针”、tmp0-3 等名称,没有人会浪费时间来尝试理解代码。我会在 C++ 中使用 SSE 作为基准性能来实现相同的代码

标签: java android optimization mathematical-optimization


【解决方案1】:

我看不到任何明显的东西。除了 Alex 所说的之外,还有两个小建议可能会有所帮助:

1) 第一个循环中的长if 语句有许多失败条件。您是否已订购它,以便最有可能失败的那个先出现?使用短路计算,越早找到false,计算整个表达式所需的工作就越少。

2) 你在两个 for 循环之外声明了很多变量,我明白你为什么这样做了。如果您将声明移动到两个循环内,那么 JVM 可能会更好地优化事物,以便尽可能在本地声明变量。

对于这两种情况,您都需要进行一些计时,看看它们是否能产生真正的影响。您可能还想使用分析器来查看代码大部分时间花在哪里。

我还有另一条评论。在这样的行中:

data[pointer + 7] = (short) ((tmp10 - tmp3) >> F3);

您正在使用&gt;&gt; 而不是&gt;&gt;&gt; 对可能的负数进行位移。如果 tmp3 > tmp10,你确定这就是你想要做的吗?

【讨论】:

  • 感谢您的建议。我会更多地研究它。
【解决方案2】:

在没有性能问题的具体证据的情况下,我可以看到一些可能的改进(尽管不确定您会从中获得多少改进)。

  1. 用变量替换重复计算。例如pointer+7 重复几次。您可以计算一次。
  2. 使用System.arraycopy() 在末尾复制数组。

【讨论】:

  • 只计算一次 pointer+7 不会加快代码速度,从而产生很大的不同,但感谢 System.arraycopy()。
  • 您需要什么样的证据?第一个 for 循环大约需要 7.3 亿纳秒,而第二个 for 循环大约需要 7.9 亿纳秒,其中 dataBlockBuffer 设置为大小为 64 的数组,每个点设置为“1”的值。
  • JIT 编译器无论如何都应该这样做!
  • @Hogan 我并不是要只替换 pointer+7,而是要替换所有重复项。很少。但是,我同意@Neil 的观点,它可能会由 JIT 处理,并且改进不会那么大。但我还是会尝试一下。
  • @Alex 实际上,替换重复项会创建更多变量并花费更多时间,因为随后必须为两个 for 循环中的每个循环更新变量。
【解决方案3】:

与其他海报一样,我认为您可以做的事情很少来优化它。

出于绝望,我可能会尝试在条件之前将所有值 (dataBlockBuffer[x​​]) 读取到局部变量中,然后将条件更改为:

如果 ((data0 | data1 | data2 | ...) == 0) ...

这样,您可能拥有更少的分支,并且条件失败的地方(大概大部分时间?)数据已经在局部变量中准备好了。

但话虽如此,我认为你不会刮胡子。

要考虑的另一件非常边缘的事情是,将 ByteBuffer 包装在一个数组周围,如果您能找到一种方法,您可能会一次读取/写入多个值。 (至少在多次写入 'dcValue' 时,您可以改为使用 long,以防 JIT 编译器不进行此优化)。

在桌面上,我可能考虑过:

  • 多线程同时处理多个块(现在大多数人至少有双核,但猜测可能不是在 Android 上);
  • 查看 JIT 编译的输出并确保 JIT 编译器没有做任何明显愚蠢的事情,然后相应地重新处理代码(您可以使用调试 JDK 执行此操作,但不知道是否有等效的Android);
  • 先进行本地化,然后使用 GPU/任何明智的编译指示,您的 C 编译器可能必须编译为特定的 SSE/等效指令(我不是这方面的专家,也不知道在这方面有多大可能)你的情况)。

无论如何,我都会将最后两个视为一种“极端”的选择——有投入大量时间而收效甚微的风险。

【讨论】:

    猜你喜欢
    • 2020-06-26
    • 2012-09-03
    • 2011-04-18
    • 1970-01-01
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多