2

假设我们有一个给定的接口:

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 次以上,很难说调用次数的上限是多少。

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

  • 答案是否取决于我们使用的两个提议的实现中的哪一个,或者无关紧要?

  • 答案是否取决于估计的呼叫次数?使用一种一种方法更好地计数多少?

4

6 回答 6

2

简短的回答:没有检查总是会更快。

分配比比较花费更少的计算时间。因此:IF 语句总是比赋值慢。

comparing有 2 个变量时,您的 CPU 将获取第一个变量,获取第二个变量,比较这两个变量并将结果存储到临时寄存器中。那是2 fetches1 compare1 store.

当您assign输入一个值时,您的 CPU 将获取 '=' 右侧的值并将其存储到内存中。那是1 fetch1 store

于 2013-10-04T13:43:23.203 回答
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 万个元素的数组才能真正看到一些东西。

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

于 2013-10-04T15:24:05.007 回答
2

一般来说,如果您需要设置一些状态,只需设置状态即可。另一方面,如果你必须做更多的事情——比如记录更改、通知更改等——那么你应该首先检查旧值。

但是,如果像您提供的方法这样的方法被非常强烈地调用,检查与非检查可能会有一些性能差异(新值是否不同)。可能的结果是:

1-a) 检查返回假
1-b) 检查返回真,赋值
2) 赋值时不检查

据我所知,写总是比读慢(一直到寄存器级别),所以最快的结果是 1-a。如果您的情况是最常见的情况是该值不会改变(“超过 50%”的逻辑还不够好,必须凭经验计算出确切的百分比)-那么您应该检查,因为这消除了多余的写入操作(赋值)。另一方面,如果值经常不同 - 分配它而不检查。

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

至于这里的布尔值与布尔值,我会说(不由自主地)应该没有性能差异。

于 2013-10-04T13:34:51.213 回答
1

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

过早的优化是万恶之源

很明显if,多做一件事就是多做一件事,但是……这并不重要。

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

于 2013-10-04T13:36:36.673 回答
0

negateWithoutCheck() 更可取,因为如果我们考虑调用次数,则 negateWithoutCheck() 只有一次调用,即 this.b = false; 其中 negateWithCheck() 与前一个相比有一个额外的。

于 2013-10-04T13:42:08.930 回答
0
  1. 我通常会认为 negateWithCheck 会稍微慢一些,因为总是有比较。还要注意在 StateKeeperOfObjects 中您正在引入一些自动装箱。'true' 和 'false' 是原始布尔值。

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

  3. 速度会稍微取决于调用的次数,但总的来说,无论调用一次还是多次,速度都应该被认为是相同的(忽略缓存、jit 等次要影响)。

在我看来,一个更好的问题是性能差异是否明显。我从事一个涉及数百万并行数值计算的科学项目。我们开始使用对象(例如 Integer、Double)并且在内存和速度方面的性能都不尽如人意。当我们将所有计算转换为基元(例如 int、double)并检查代码以确保我们没有通过自动装箱引入任何时髦的东西时,我们看到了巨大的性能提升(内存和速度)。

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

于 2013-10-04T13:39:52.370 回答