在java中检查布尔值比设置布尔值快吗?

在java中检查布尔值比设置布尔值快吗?,java,performance,Java,Performance,这: 与此相比: if (var) { var = false; } 是否存在速度差异?第一个代码包含一个比较,因此您的编译器可能会生成如下所示的java字节码: var = false; 对于第二个代码生成的字节码较短,因为缺少比较: public static void main(java.lang.String[]); Code: 0: iconst_1 1: istore_1 2: iload_1

这:

与此相比:

if (var) {
    var = false;
}

是否存在速度差异?

第一个代码包含一个比较,因此您的编译器可能会生成如下所示的java字节码:

var = false;
对于第二个代码生成的字节码较短,因为缺少比较:

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1      
       1: istore_1      
       2: iload_1       
       3: ifeq          8
       6: iconst_0      
       7: istore_1      
       8: return    
在第一个示例中,虚拟机执行8个命令所需的时间比在第二个示例中执行4个命令所需的时间多。虽然这个差别不应该太大,但第二个代码更清楚


将代码放在一个简单的main方法中并编译该类。然后运行命令提示符并切换到
java/bin
目录。要反汇编类,请调用
javap-cpath/To/YourClass.class>>path/To/bytecode.txt
。bytecode.txt将包含您的类的java字节码。

有几件事需要考虑,对实际性能的最终影响是您需要根据您的用例来衡量的。我认为这是一种你经常发现的方法:

  • 分支预测-如果var几乎总是错误的,这就是代码所建议的,那么分支预测几乎总是正确的。如果字段经常更改,则这将成为一个经常预测失误的分支,并且成本高昂

  • 读取未命中-如果var大部分是读取的(并且读取了很多),那么避免无故更改可以帮助您的软件不会使它所在的缓存线无效。如果您对它进行写入,那么读取它(以及同一缓存线上的任何内容)的每个其他内核都需要获得一个新的副本,该副本将经历读未命中。这意味着,为了使读取具有更一致的速度,上述方法可能值得降低速度

  • 写入成本与读取成本-如果var是易变的,那么它的写入是一个负载存储障碍,这相当昂贵。相比之下,读取volatile(负载屏障)是相当便宜的(对于经常使用且几乎没有更改的值的缓存命中)。相比之下,这可以使分支非常便宜

  • 这是一个优化的人,例子可以在JDK(Irc)中找到,我想你有理由考虑它。

    < p>“速度差”,如果有的话,将完全取决于JVM。任何优秀的编译器都应该能够优化测试,在这一点上,两者是相同的

    例外情况:如果
    var
    被声明为
    volatile
    ,条件版本将始终较慢


    在任何情况下,如果性能至关重要,最好的选择是在预期条件下(机器、系统、JVM、典型负载等)对其进行测量。

    关于这一点,我在游戏中迟到了,但我编写这个测试类是为了回答一个类似的问题

      public static void main(java.lang.String[]);
        Code:
           0: iconst_1      
           1: istore_1      
           2: iconst_0      
           3: istore_1      
           4: return      
    
    这是另一个廉价的“基准”(几乎不配使用这个术语)。对我来说,猴子扳手基准的结果对于OP的问题并没有明确地说出来,所以我想我也应该把它放在这里

    结果(在这些条件下,并且仅在很少的测试中):在编写局部布尔值之前检查它比在没有必要的情况下经常编写要好

    if(BoolTest)                    443                     When False      0.00000443
    if(BoolTest)                    443                     When True
    
    if(BoolTest == false)           443                     When False
    if(BoolTest == false)           442                     When True
    
    if(!BoolTest)                   438                     When False      
    if(!BoolTest)                   441                     When True
    
    (BoolTest ? Var++ : Var--)      435                     When True
    (BoolTest ? Var++ : Var--)      435                     When False
    
    publicstaticvoidmain(字符串[]args){
    最终随机数r=新随机数(0);
    布尔b=假;
    布尔决策;
    长启动时间;
    长时间;
    startTime=System.currentTimeMillis();
    对于(长i=0;i<100000000;i++){
    decision=r.nextDouble()>0.1;//在大多数情况下都是正确的。
    如果(决定){
    //如果(!b){
    b=正确;
    // }
    }
    }
    endTime=System.currentTimeMillis();
    System.err.println(endTime-startTime);
    系统错误println(b);
    系统出口(0);
    }
    直截了当地写道(ms):
    18139
    18140
    18196
    (18220)
    (18181)
    ----------
    平均值3:18158.333333
    平均值5:18175.2
    写前检查(ms):
    18097
    18109
    18115
    (18129)
    (18133)
    ----------
    平均值3:18107
    平均值5:18116.6
    通过检查,仅获取该百分比(3个样本):99.7173015144561725621844882974
    通过检查,它仅获取此%(5个样本):99.677582640080949480170782164708
    

    通过检查,大约需要99.7%的书写时间。如果写操作在其他情况下不必要地发生,则通常。在这个蹩脚的“基准测试”中,也就是说。

    但复杂性增加了。速度是微观的,与此无关。如果需要将
    var
    设置为
    false
    ,请进行设置。如果您之前需要检查它当前是否为
    true
    ,那么请先检查。添加可能触发错误分支预测的分支是一件非常可怕的事情。通常更有效的方法是做更多的工作来避免分支(1/0的乘法等),因为这会变得多么糟糕。相关的,@ZouZou(以及Mena)你的评论是在说教,而不是在告知。这不是OP是否应该关心的问题。也不是哪种方法更容易阅读。字节码不是性能的好指标,除非你计划把大部分时间花在解释器上。你错了。易失性读取比写入便宜得多。这是一个经过测试的针对易失性字段写入的优化。@NitsanWakart也许我不清楚。如果
    var
    被声明为
    volatile
    ,那么OP的语句
    If(var){var=false;}
    总是比
    var=false慢var
    ;因此产生了读和写的成本,而不仅仅是写。我没有说明volatile读取和写入之间的性能差异。这种说法“将始终较慢”是不正确的。只有当var为true时才是正确的,它假设OP不与我们共享代码。当var已经为false时,条件写入总是比IME更快(因为volatile写入不会发生,而且比读取要昂贵得多)。感谢您的澄清,我现在了解到您
    if(BoolTest)                    443                     When False      0.00000443
    if(BoolTest)                    443                     When True
    
    if(BoolTest == false)           443                     When False
    if(BoolTest == false)           442                     When True
    
    if(!BoolTest)                   438                     When False      
    if(!BoolTest)                   441                     When True
    
    (BoolTest ? Var++ : Var--)      435                     When True
    (BoolTest ? Var++ : Var--)      435                     When False
    
    public static void main(String[] args) {
    
        final Random r = new Random(0);
    
        boolean b = false;
        boolean decision;
    
        long startTime;
        long endTime;
    
        startTime = System.currentTimeMillis();
        for (long i = 0; i < 1000000000; i++) {
            decision = r.nextDouble() > 0.1; // Will be true MOST of the time.
    
            if (decision) {
    
                // if (!b) {
                    b = true;
                // }
    
            }
        }
        endTime = System.currentTimeMillis();
    
        System.err.println(endTime - startTime);
        System.err.println(b);
    
        System.exit(0);
    }
    
    With bluntly writing (ms):
    18139
    18140
    18196
    (18220)
    (18181)
    ----------
    Average of 3: 18158.333333333333333333333333333
    Average of 5: 18175.2
    
    
    With checking before writing (ms):
    18097
    18109
    18115
    (18129)
    (18133)
    ----------
    Average of 3: 18107
    Average of 5: 18116.6
    
    
    With checking, it only takes this % (3 samples): 99.71730151445617255621844882974
    With checking, it only takes this % (5 samples): 99.677582640080989480170782164708