为什么按位运算符在比较布尔值时比;“正常”;用Java写的?

为什么按位运算符在比较布尔值时比;“正常”;用Java写的?,java,comparison,conditional,bit-manipulation,operator-keyword,Java,Comparison,Conditional,Bit Manipulation,Operator Keyword,假设有两个函数,它们的作用基本相同,就是用AND或运算符比较两个随机布尔值。但是一个函数使用普通的条件运算符&和| |,另一个函数使用位运算符&和| 我认为这两个功能当然需要相同的时间来完成,但是它们不需要。采用位比较的方法比采用“正常”条件运算符的方法多花五分之一的时间 我感到困惑,并进行了一些研究,在Oracle的Java文档中找到了以下条件运算符的定义: The && and || operators perform Conditional-AND and Conditio

假设有两个函数,它们的作用基本相同,就是用AND或运算符比较两个随机布尔值。但是一个函数使用普通的条件运算符&和| |,另一个函数使用位运算符&和|

我认为这两个功能当然需要相同的时间来完成,但是它们不需要。采用位比较的方法比采用“正常”条件运算符的方法多花五分之一的时间

我感到困惑,并进行了一些研究,在Oracle的Java文档中找到了以下条件运算符的定义:

The && and || operators perform Conditional-AND and Conditional-OR operations on two boolean expressions. These operators exhibit "short-circuiting" behavior, which means that the second operand is evaluated only if needed.
&& Conditional-AND
|| Conditional-OR
对于按位运算符:

The Java programming language also provides operators that perform bitwise and bit shift operations on integral types. 
The bitwise & operator performs a bitwise AND operation.

The bitwise | operator performs a bitwise inclusive OR operation.
嗯,这些定义并没有真正让我满意,我的问题仍然没有得到回答。 所以我做了更多的研究,在这里发现了一个关于stackoverflow的问题,关于Java()中逐位运算符对布尔值的影响,但这也没有真正回答我的问题

这是我测试它的代码,它使用一个随机数生成器来获得随机布尔值,以便相互比较,并在这一段时间内循环

import java.util.Random;

public class Main {

    private static final long LOOPS = 100000000L;
    private static Random rng = new Random();

    public static final void main(String[] args)
    {
        System.out.println("Bitwise operator: "+loopWithBitwiseOperator(LOOPS));
        System.out.println("Normal operator: "+loopWithNormalOperator(LOOPS));
    }

    public static long loopWithNormalOperator(long loops)
    {
        long startTime = System.currentTimeMillis();

        for (long i = 0L; i < loops; i++)
        {
            if (rng.nextBoolean() || rng.nextBoolean())
            {
                i++;
            }

            if (rng.nextBoolean() && rng.nextBoolean())
            {
                i++;
            }
        }

        return System.currentTimeMillis()-startTime;
    }

    public static long loopWithBitwiseOperator(long loops)
    {
        long startTime = System.currentTimeMillis();

        for (long i = 0L; i < loops; i++)
        {
            if (rng.nextBoolean() | rng.nextBoolean())
            {
                i++;
            }

            if (rng.nextBoolean() & rng.nextBoolean())
            {
                i++;
            }
        }

        return System.currentTimeMillis()-startTime;
    }
}
因此,按位运算符实际上比“正常”运算符慢。我的第一个想法是,可能随机数生成器生成的布尔值在运行时会以某种方式被视为不同的,当与另一个布尔值进行比较时,它实际上会进行比需要的更多的逐位比较,因为它未定义实际保存的方式。但后来我认为这可能是因为条件运算符的“短路”机制,这可能不适用于按位运算符,因为按位运算符可能实际上需要两个值进行比较

我的假设正确吗?为什么会发生这种情况?另一个不代表Java逻辑运算符实际行为的特定行为示例

一如既往,感谢您事先提供的帮助和澄清

编辑:

按照评论中的要求,我尝试切换通话,并加入一些热身阶段,但仍然没有太大变化:

Normal operator: 1801
Bitwise operator: 2433

路易斯·沃瑟曼的评论是正确的诊断。对随机数生成器的调用次数强烈影响性能,因此短路非常重要。我修改了程序,使用以下两种方法计算调用数:

private static boolean normalCountedRandom() {
  normalCount++;
  return rng.nextBoolean();
}
我还在每次运行中进行了多次测量,以消除任何启动效果问题。典型输出为:

Bitwise operator: 1985
Normal operator: 1560
Bitwise operator: 1967
Normal operator: 1547
Bitwise operator: 2046
Normal operator: 1557
Bitwise operator: 2009
Normal operator: 1553
Bitwise Random calls: 800006428
Normal Random calls: 600030931
4:3的比例与预期一致。每个
if
执行一个强制调用。如果使用短路操作员,其中只有一半会导致第二次呼叫,预计每个
If
呼叫1.5次,而没有短路的情况下,每个
If
呼叫2次。这个比率与性能比率类似,假设调用占大部分时间,但不是全部时间

短路行为记录在JLS中。从技术上讲,当应用于布尔操作数时,
&
|
是有效的。“正常”运算符为和运算符


通常最好避免迭代次数取决于
随机结果,尽管在这种情况下,迭代次数足够多,运行之间的差异很小。

尝试颠倒测试顺序,或交替运行测试。一般来说,我希望你先跑的那辆会显得慢一些。->规则1:始终包括热身phase@PatriciaShanahan我已经试过了,最初我将按位的一个作为第二个,然后切换,没有区别我强烈怀疑
random.nextBoolean()的成本使用
&&
避免了一半的时间,
|
足够高,超过了位运算符避免这些分支的好处。如果你先将随机布尔存储到变量中,我倾向于期望得到不同的结果。@louiswasserman的评论当然是正确的答案。你应该给它一个答案,这样就可以投票了!
Bitwise operator: 1985
Normal operator: 1560
Bitwise operator: 1967
Normal operator: 1547
Bitwise operator: 2046
Normal operator: 1557
Bitwise operator: 2009
Normal operator: 1553
Bitwise Random calls: 800006428
Normal Random calls: 600030931