问题 检查布尔值比在java中设置布尔值更快?


这个:

if (var) {
    var = false;
}

对此:

var = false;

有速度差吗?


11017
2018-04-22 19:19


起源

但复杂性增加了。 - Braj
我希望第二个版本更快。 - Louis Wasserman
速度是微观的,与此无关。如果你需要设置 var 至 false,设置它。如果你需要检查它是否是当前的 true之前,然后先做。 - Mena
添加一个可能触发错误分支预测的分支是一件非常可怕的事情。做更多的工作通常更有效,以避免分支(乘以1/0等)与这可能有多糟糕。 - Blindy
有关, stackoverflow.com/questions/11227809/... - Blindy


答案:


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

  1. 分支预测 - 如果var几乎总是假的,这就是代码所暗示的,分支预测器几乎总是正确的。如果该字段经常变化,那么这将成为一个经常被错误预测的分支并且将是昂贵的。

  2. 读取未命中 - 如果var主要是读取(并且读取很多),那么避免无故更改可以帮助您的软件不会使其所在的缓存行无效。如果你写它的每个其他核心读取它(以及同一缓存行上的任何内容)将需要获得一个新的副本遇到读取错过。这意味着为了使读取具有更一致的速度,上述方法可能值得变慢。

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

这是人们的优化,可以在JDK(IIRC)中找到示例,我假设您有理由考虑它。


7
2018-04-22 20:57





第一个代码 包含比较,因此您的编译器可能会生成一个类似于以下内容的java字节码:

  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    

为了 第二个代码 生成的字节码较短,因为缺少比较:

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1      
       1: istore_1      
       2: iconst_0      
       3: istore_1      
       4: return      

虚拟机在第一个示例中执行8个命令比在第二个示例中执行8个命令需要更多时间。虽然这种差异不应该高,但第二代码更清楚。

将代码放在一个简单的main方法中并编译该类。然后运行命令提示符并更改为 java/bin 目录。要反汇编你的课堂电话 javap -c path/to/YourClass.class >> path/to/bytecode.txt。 bytecode.txt将包含您的类的java字节码。


5
2018-04-22 20:13



除非您计划将大部分时间花在翻译中,否则字节码不是表现良好的表现。 - Nitsan Wakart


“速度差异”(如果有的话)将完全取决于JVM。任何体面的编译器都应该能够优化测试,此时两者是相同的。

例外:如果 var 宣布 volatile 条件版本总是会慢一些。

无论如何,如果性能至关重要,最好的选择是 测量 它在预期的条件下(机器,系统,JVM,典型负载等)。


1
2018-04-23 00:48



你错了。易失性读取比写入便宜得多。这是针对易失性字段写入的经过测试的优化。 - Nitsan Wakart
@NitsanWakart也许我不清楚。如果 var 宣布 volatile那么OP的声明 if (var) { var = false; } 永远都会慢下来 var = false; 因为编译器不会优化读取 var;因此产生了阅读的成本 和 写而不是写。我没有说明易失性读写之间的性能差异。 - Chuck Batson
这句话“将永远慢”并非如此。当var为真时它才正确,它假定OP不与我们共享的代码。当var已经为假时,条件写入总是更快IME(因为不会发生易失性写入,并且它比读取要贵得多)。 - Nitsan Wakart
谢谢你的澄清,我现在明白你说当写入成本>读取成本时,“测试和跳过”路径可能比直接“写入”路径更快。我同意这一点,而且还取决于分支机构的性能。 - Chuck Batson


我在这个游戏上玩的很晚,但我写了这个测试类来回答类似的问题。

package SpeedTests;

import java.text.NumberFormat;
import java.util.Locale;

public class BooleanSpeedTest {

    public static void main(String[] args) {
        boolean BoolTest = true;
        long LongLoop = 100000;
        long TrueLoopCount = 0;
        long FalseLoopCount = 0;
        long TimeTotal = 0;

        long startTime;
        long endTime;

        for(int intLoopA = 1; intLoopA < 6; intLoopA++) {
            LongLoop = LongLoop * 10;
            TimeTotal = 0;
            System.out.println("");
            System.out.print(
                    NumberFormat.getNumberInstance(Locale.US).format(LongLoop) + " - ");

            for(int intLoopM = 0; intLoopM < 20; intLoopM++) {
                TrueLoopCount = 0;
                FalseLoopCount = 0;
                startTime = System.currentTimeMillis();

                for(long LoopCount = 0; LoopCount < LongLoop; LoopCount++) {
                    if(!BoolTest) {
                        TrueLoopCount++;
                    }
                    else
                        FalseLoopCount++;   
                }
                endTime = System.currentTimeMillis();
                System.out.print( (endTime - startTime) + "ms ");
                TimeTotal += ((endTime - startTime) );    
            }

            System.out.print(" AVG: " + (TimeTotal/20));
        }
    }
}

我的结果:                                 平均时间/十亿(ms)注意每个循环的时间

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

1
2017-08-09 21:09