短路评估对性能的影响

时间:2016-06-17 22:22:07

标签: java performance short-circuiting

免责声明:我在逆向工程字节码方面没有太多经验,所以如果可以轻松地对我进行操作,请不要过于苛刻。回答我的问题。

在现代处理器上,如果预测失败,分支可能会非常昂贵(参见Why is it faster to process a sorted array than an unsorted array?)。

让我们说我在Java中有一些像这样的短路评估:

if (condition && (list!=null) && (list.size()>0)) /* Do something */ ;

这基本上相当于一堆像这样的分支:

if (condition) {
    if (list!=null) {
        if (list.size()>0) {
            // Do something
        }
    }
}

或者Java是否有其他方法可以更巧妙地进行短路?

换句话说,最好通过重写这样的行来避免至少一个分支:

if ((condition & (list!=null)) && (list.size()>0)) /* Do something */ ;

因为简单的list!=null - 检查比可能预测错误的分支要便宜得多吗?

(显然,我无法摆脱第二个&&而不会冒NullPointerException的风险。)

现在,在我用#34之类的陈述撕成碎片之前,过早的优化是所有邪恶的根源!",请记住,这是一般编码习惯之间的选择(总是使用短 - 电路与从不使用短路,除非需要),这将影响我的代码的所有,所以确保我在这里使用正确的习惯绝对值得花些时间考虑。

2 个答案:

答案 0 :(得分:1)

没有here提及任何类型的分支。这仅仅是表达式评估,&&语句中的if与表达式中的&&没有区别。你能用下面的代码问同样的问题吗?

boolean isValid = condition && (list!=null) && (list.size()>0);
if (isValid) {
    ...
}

这基本上就是这样,表达式被评估,然后发生分支。

答案 1 :(得分:1)

我做了一个简单的测试

int sum = 0;
Random rnd = new Random(1);

int[] a = new int[1000];
int[] b = new int[1000];
for (int i = 0; i < 1000; ++i)
{
    a[i] = rnd.nextInt(100);
    b[i] = rnd.nextInt(100);
}

long started = System.nanoTime();

for (int i = 0; i < 1000000; ++i)
{
    for (int j = 0; j < 1000; ++j)
    {
        if (a[j] < 50 && b[j] < 5)// change "&& b[j] < 5"
        {
            sum++;
        }
    }
}

long ended =  System.nanoTime();
System.out.println((ended - started)/1000000 + "  " + sum);

结果非常随机:

            &&      &
b[j] < 5    1450    1360
b[j] < 50   1330    1610
b[j] < 500  1310    1450
j < 50      2200    920
j < 500     1410    1730
j < 5000    1180    1040
j < i       1180    2050
i < j       2290    1450

这些是来自更多运行的最低值,我确保它们是可重复的。实际时间因运行而异。作为一个经验法则,我会避免过于花哨,坚持&amp;&amp; amp;希望优化&#34;在那里&#34;尽全力。有关于optimizations

的精彩视频

修改

正如Dici指出的那样,应该完成JVM的热身。似乎函数的第一次和第二次调用与其他函数不同。当增加循环次数时,规则也始终适用。所以我重新测试了那个......又弄得一团糟。平均速度提高约2倍,但再次变得混乱。并且优化更加不稳定,通常没有一个,但两个典型的时间值甚至50%不同。我看了JMH,很好的框架。我可以测量一下&amp;比&amp;&amp;更快如果我真的尝试过(在不同的系统,不同的硬件等等,很多工作)。但这不是问题。问题是,如果我更换&amp;&amp; for&amp;在我的程序中,我可以期待它更快还是更慢?答案是 - 你不能指望任何事情,你必须衡量它。

<强> EDIT2

在这种情况下,我认为这是浪费时间,但为了维护我的可信度,我测量了标准偏差(又称一个西格玛)。在几次运行中,值表现得非常好,并且它们在数千次运行中表现良好,这并不令人意外(我没有在JVM热身后显示结果,因为它们表现不佳并且统计数据将是不可避免的)。有趣的是,所有结果都比我预测的结果快7%左右,对于大多数值来说,这超过了5西格玛。从几次尝试看来,网页浏览器标签似乎影响整个系统的速度,不,我不会做统计数据来确认我的观察。

            &&          &
b[j] < 5    1333(14)    1265(25)
b[j] < 50   1231(11)    1514(13)
b[j] < 500  1223(9)     1360(13)
j < 50      2069(74)    842(11)
j < 500     1294(12)    1631(17)
j < 5000    1089(9)     957(8)
j < i       1086(8)     1907(23)
i < j       2164(16)    1357(14)