额外的'if checks'如果已经设置了值 - 什么更快,什么使用更多资源?

时间:2013-10-04 13:25:53

标签: java jvm micro-optimization premature-optimization microbenchmark

假设我们有一个给定的界面:

public interface StateKeeper {

    public abstract void negateWithoutCheck();

    public abstract void negateWithCheck();

}

以及以下实施:

class StateKeeperForPrimitives implements StateKeeper {
    private boolean b = true;

    public void negateWithCheck() {
        if (b == true) {
            this.b = false;
        }
    }

    public void negateWithoutCheck() {
        this.b = false;
    }
}

class StateKeeperForObjects implements StateKeeper {
    private Boolean b = true;

    @Override
    public void negateWithCheck() {
        if (b == true) {
            this.b = false;
        }
    }

    @Override
    public void negateWithoutCheck() {
        this.b = false;
    }
}

此外假设方法negate*Check()可以被多次调用1次,并且很难说调用次数的上限是多少。

  • 问题是两种实现中哪种方法“更好” 根据执行速度,垃圾收集,内存分配等 - negateWithChecknegateWithoutCheck

  • 答案取决于提议的两个 我们使用的实现或无关紧要?

  • 答案是否取决于估计的通话次数?对于使用一个第一个方法更好的数量?

6 个答案:

答案 0 :(得分:2)

通常,如果需要设置某个状态,只需设置状态即可。另一方面,如果你需要做更多的事情 - 比如记录变更,告知变更等等 - 那么你应该首先检查旧值。

但是,如果您提供的方法被非常强烈地调用,则检查与非检查之间可能存在一些性能差异(新值是否不同)。可能的结果是:

1-a)检查返回false
 1-b)检查返回true,赋值为
 2)没有检查分配值

据我所知,写入总是慢于读取(一直到寄存器级别),因此最快的结果是1-a。如果你的情况是最常见的事情是价值不会改变('超过50%'的逻辑不够好,确切的百分比必须根据经验计算) - 那么你应该去检查,因为这消除了冗余写入操作(值分配)。另一方面,如果价值与经常不同,则不经检查就分配。

您应该测试具体案例,进行一些分析,并根据结果确定最佳实施。对于这种情况,没有一般的“最佳方式”(除了“只是设置状态”)。

至于boolean vs Boolean,我会说(在我的脑海中)应该没有性能差异。

答案 1 :(得分:2)

简短回答:不检查总是会更快。

与比较相比,分配计算时间要少得多。因此:IF语句总是慢于赋值。

comparing 2个变量时,您的CPU将获取第一个变量,获取第二个变量,比较这些变量并将结果存储到临时寄存器中。这是2 fetch es,1 compare1 store

当你assign一个值时,你的CPU将获取'='右边的值并将其存储到内存中。这是1 fetch1 store

答案 2 :(得分:2)

使用支票时,可能可能会带来轻微的性能优势。我非常怀疑它在任何现实生活中都很重要。

  过早的优化是万恶之源(Donald Knuth)


您可以衡量两者之间的差异。我要强调,这类事情很难可靠地衡量。

这是一种简单易行的方法。如果检查识别出不必更改值,则可以希望获得性能优势,从而节省昂贵的内存写入。所以我相应地更改了你的代码。

interface StateKeeper {

    public abstract void negateWithoutCheck();

    public abstract void negateWithCheck();

}

class StateKeeperForPrimitives implements StateKeeper {

    private boolean b = true;

    public void negateWithCheck() {
        if (b == false) {
            this.b = true;
        }
    }

    public void negateWithoutCheck() {
        this.b = true;
    }
}

class StateKeeperForObjects implements StateKeeper {

    private Boolean b = true;

    public void negateWithCheck() {
        if (b == false) {
            this.b = true;
        }
    }

    public void negateWithoutCheck() {
        this.b = true;
    }
}

public class Main {

    public static void main(String args[]) {

        StateKeeper[] array = new StateKeeper[10_000_000];

        for (int i=0; i<array.length; ++i)
            //array[i] = new StateKeeperForObjects();
            array[i] = new StateKeeperForPrimitives(); 

        long start = System.nanoTime();

        for (StateKeeper e : array)
            e.negateWithCheck();
            //e.negateWithoutCheck();

        long end = System.nanoTime();

        System.err.println("Time in milliseconds: "+((end-start)/1000000));
    }
}

我得到以下内容:

           check  no check
primitive   17ms    24ms
Object      21ms    24ms

当检查始终是多余的时,我没有发现检查相反的任何性能损失,因为总是必须更改值。

两件事:(1)这些时间不可靠。 (2)该基准测试远非任何实际应用;我不得不制作一个包含1000万个元素的数组来实际看到的东西。

我只是选择没有检查的功能。我非常怀疑在任何实际应用程序中,您将从具有检查的功能中获得任何可衡量的性能优势但该检查容易出错且是更难阅读。

答案 3 :(得分:1)

直到今天我才看到很少的答案和评论重复

  

过早优化是所有邪恶的根源

显然,一个if语句更多是要做的事情,但是......这并不重要。

垃圾收集和内存分配......这里不是问题。

答案 4 :(得分:0)

  1. 我通常会认为negateWithCheck稍微慢一点,因为总是有比较。另请注意,在StateKeeperOfObjects中,您正在引入一些自动装箱。 'true'和'false'是原始的布尔值。

  2. 假设您修复StateKeeperOfObjects以使用所有对象,那么可能,但很可能不会引人注意。

  3. 速度将略微取决于呼叫次数,但一般来说,无论您是呼叫一次还是多次,速度都应该被认为是相同的(忽略辅助效果,如缓存,jit等)。

  4. 在我看来,一个更好的问题是性能差异是否明显。我正在研究一项涉及数百万并行数值计算的科学项目。我们开始使用Objects(例如Integer,Double),并且在内存和速度方面都没有达到理想的性能。当我们将所有计算切换为基元(例如int,double)并查看代码以确保我们没有通过自动装箱引入任何时髦时,我们看到了巨大的性能提升(内存和速度)。

    我非常喜欢避免过早优化,除非它是“简单”实现的东西。只是要警惕后果。例如,您是否必须在数据模型中表示空值?如果是这样,你如何使用原语呢?使用NaN可以很容易地完成双打,但布尔人呢?

答案 5 :(得分:0)

negateWithoutCheck()是首选,因为如果我们考虑调用次数,那么negateWithoutCheck()只有一次调用,即this.b = false;其中negateWithCheck()有一个额外的前一个。