【问题标题】:Why does adding an If-statement inside a this method slow it down so drastically?为什么在 this 方法中添加 If 语句会大大减慢它的速度?
【发布时间】:2012-08-27 18:06:55
【问题描述】:

我在answering another question 中遇到了这个问题。我试图诊断哪些代码更改对速度的影响更大。我在 for 循环中使用了一个布尔标志来切换使用辅助方法来构造 Color。

有趣的行为是,当我决定哪一个更快并删除如果代码的速度放大 10 倍时。之前用了 140 毫秒,之后只用了 13 毫秒。我应该只从循环中删除大约 7 个计算中的一个。为什么速度会如此大幅提升?

慢代码:(helperMethods 为假时运行时间为 141 毫秒) *参见编辑 2

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    for(int j = 0; j < colorPixels.length;j++){
        if(helperMethods){
            colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]), Color.red(colorPixels[j]), Color.green(colorPixels[j]), Color.blue(colorPixels[j]));
        } else colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

快速代码:(运行时间为 13 毫秒)

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    for(int j = 0; j < colorPixels.length;j++){
        colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

编辑: 问题似乎不在于 if 在循环内。如果我将if 提升到循环之外。代码运行速度稍快,但仍以 131 毫秒的速度运行:

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    if (helperMethods) {
        for (int j = 0; j < colorPixels.length;j++) {
            colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]),
                                        Color.red(colorPixels[j]),
                                        Color.green(colorPixels[j]),
                                        Color.blue(colorPixels[j]));
        }
    } else {
        for (int j = 0; j < colorPixels.length;j++) {
             colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
        }
    }

    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

编辑 2: 我很笨。真的真的很笨。在调用堆栈的前面,我使用另一个布尔标志在使用此方法和使用另一个使用getPixel 而不是getPixels 的方法之间切换。我为所有具有helperMethod 参数的调用设置了错误的标志。当我对没有helperMethod 的版本进行新调用时,我做对了。性能提升是因为 getPixels 而不是 if 语句。

实际慢代码:

public static void applyAlphaGetPixel(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    for(int y=0; y < h; ++y) {
        for(int x=0; x < w; ++x) {
            int pixel = b.getPixel(x,y);
            int finalPixel;
            if(helperMethods){
                finalPixel = Color.argb(Color.alpha(bAlpha.getPixel(x,y)), Color.red(pixel), Color.green(pixel), Color.blue(pixel));
            } else{
                finalPixel = bAlpha.getPixel(x,y) | (0x00FFFFFF & pixel);
            }
            b.setPixel(x,y,finalPixel);
        }
    }
}

注意:所有速度均为 100 次运行的平均值。

【问题讨论】:

  • if 语句可能使代码更难优化。
  • Traceview 可能会欺骗您,因为这会禁用 JIT。
  • 请提供更详细的信息。在什么情况下是什么速度?刚刚删除了 if 或 if 和它的主体?
  • helperMethods 是整个循环的真/假。所以不能是分支预测。

标签: java android performance


【解决方案1】:

在“快速代码”中,您永远不会运行语句

colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]), Color.red(colorPixels[j]), Color.green(colorPixels[j]), Color.blue(colorPixels[j])); 

但在“慢代码”中,如果布尔值设置为 true,则至少在运行此附加语句时会延长时间。如果您的条件始终为假,则在循环的每次迭代中检查 if 语句大约 7 次。尝试将 if 放在循环之外。

【讨论】:

  • 我已经尝试过了,代码仍然以大约缓慢的时间运行。它快了 8 毫秒。
  • 这很有趣。大 O 分析... :)
  • 我不认为它在技术上是大 O,因为它是由算法运行的时间与数据集相比定义的。
  • 正确。但是它必须是消耗时间的东西,可能在较低级别的某个地方,这对我来说听起来像是错误
【解决方案2】:

尝试将条件提升到循环之外:

if (helperMethods) {
    for (int j = 0; j < colorPixels.length;j++) {
        colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]),
                                    Color.red(colorPixels[j]),
                                    Color.green(colorPixels[j]),
                                    Color.blue(colorPixels[j]));
    }
} else {
    for (int j = 0; j < colorPixels.length;j++) {
         colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
}

【讨论】:

  • 这很有趣。好像也有同样的表现。在 143 毫秒内以 helperMethods false 运行。
  • 分支预测应该可以很好地处理它......嗯。
【解决方案3】:

可能是您的分析代码让您感到困惑。尝试隔离您要分析的代码部分并仅测量该部分,避免在您的案例中创建位图等 GCable 操作。

如果我用

调用你的测试代码
testing.loadDrawable(this, false, true, false)

它运行缓慢。但是如果我用

来称呼它
testing.loadDrawable(this, true, true, false)

这是一个类似(更糟)的数字。所以 useGetPixels 让一切变得不同。我猜这会将位图数据放入本地缓冲区并稍后设置结果。

【讨论】:

  • 如果问题出在 GC 上,那是否意味着只有第一次运行会很快,因为只有后面的运行会被 GC 清除较早的测试所拖累?这也是我听说过的 GC 对性能的最大影响,而且它似乎是凭空出现的。我如何预测 GC 何时会导致如此严重的问题?这感觉就像我接近答案,但我仍然想知道到底发生了什么。
  • 在这个答案中,我没有声称问题是由于 GC 造成的,但是您的代码太乱了,并且没有正确进行分析。我相信您的问题是useGetPixels,当您启用它时,运行时间会增加。否则 if 不会改变运行时间。
  • 所以我很困惑。您是说 getPixels 的运行时间增加了?对我来说,每次都更快。你是“快速”版本是loadDrawableHard(this,true,true)。我更改了名称,这样我就可以很容易地分辨出哪个是硬编码的。在这个版本中,除了调用applyAlphaGetPixelsHard() 和“applyGetPixelHard()”之外,所有代码都是相同的。如果我在 useGetPixels 设置为 false 的情况下运行它,我看不到同样的改进。我最初写loadDrawableHard() 只使用applyAlphaGetPixelsHard(),但添加了applyGetPixelHard() 以消除可能的差异。
  • 我不敢相信我做了什么。如果您查看我的所有电话,他们都将useGetPixels 设置为false。我的分析代码没有问题,我只是在整个愚蠢的时间里分析了错误的东西。
  • 这就是我从一开始就想说的,但我想你必须看到它才能理解。当我说您的分析代码让您感到困惑时,我想说代码太乱了,很难看到您如何调用这些函数。 GC 的事情只是一个附带通知。
【解决方案4】:

在您的快速代码中,您根本不使用 Color 类。我假设这个类的初始化需要一些时间,它有很多静态方法和静态代码。

您可以尝试执行以下操作: 确保在进行测试之前 Color 类已完全加载并初始化(在调用 applyAlphaGetPixels() 方法之前,您可以从 Color 类中调用任何静态方法)。然后运行您的测试并比较结果。

【讨论】:

  • 这不会只影响第一次在程序实例中运行测试吗?
  • 它很可能只影响生产程序中的第一次运行,但在您的测试运行中可能会产生不同的影响
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-07
  • 1970-01-01
  • 2019-05-13
相关资源
最近更新 更多