首页 文章

为什么按位运算符在比较布尔值时比在Java中的“普通”运算符慢?

提问于
浏览
0

假设如下:你有两个函数,它们都做了基本相同的事情,即将两个随机布尔值与AND和OR运算符进行比较 . 但是一个函数用普通的条件运算符&&和||来做,另一个用位运算符&和| .

我认为这两个功能当然需要相同的时间来完成,他们不会这样做 . 具有按位比较的那个比使用"normal"条件运算符的时间多五分之一 .

我很困惑并做了一些研究,并在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中的布尔运算效果的问题(Effect of a Bitwise Operator on a Boolean in Java),但这并没有真正回答我的问题 .

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

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;
    }
}

我收到了以下输出:

Bitwise operator: 2502
Normal operator: 1806

因此,按位运算符实际上比“正常”运算符慢 . 我的第一个想法是,随机数生成器生成的布尔值可能在某种程度上在运行时被视为不同,并且当与另一个布尔值进行比较时,它实际上按位比较比需要更多,因为未定义它是如何实际保存的 . 但后来我认为它可能会因为条件运算符的“短路”机制而发生,这可能不适用于可能实际上需要两个值进行比较的按位运算符 .

我的假设是否正确?为什么会这样?另一个特定于示例的行为并不代表Java逻辑运算符的实际行为?

一如既往,感谢任何帮助和提前澄清 .

编辑:

根据评论中的要求,我试图切换调用并包含某种预热阶段,但仍然没有太大的变化:

Normal operator: 1801
Bitwise operator: 2433

1 回答

  • 2

    Louis Wasserman的评论是正确的诊断 . 对随机数发生器的调用次数强烈影响性能,因此短路很重要 . 我修改了程序,使用以下几种方法计算调用次数:

    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 . 这个比率与性能比相似,假设呼叫占了大部分时间,但不是全部时间 .

    JLS中记录了短路行为 . 从技术上讲,当应用于布尔操作数时, &|Boolean Logical Operators . "normal"运算符是Conditional-AndConditional-Or运算符 .

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

相关问题