15

This:

if (var) {
    var = false;
}

Versus this:

var = false;

Is there a speed difference?

a113nw
  • 1,312
  • 1
  • 16
  • 26
  • but complexity is increased. – Braj Apr 22 '14 at 19:20
  • I would expect the second version to be faster. – Louis Wasserman Apr 22 '14 at 19:21
  • 3
    Speed is microscopic and not relevant for this. If you need to set `var` to `false`, set it. If you need to check whether it's currently `true` before, then do that first. – Mena Apr 22 '14 at 19:21
  • 2
    Adding a branch that can potentially trigger false branch prediction is a horrible, horrible thing to do. It's usually more effective to do more work to avoid branches (multiplication with 1/0 etc) with how bad this can get. – Blindy Apr 22 '14 at 19:29
  • Speed aside, the first version is redundant and the second version is clearer. But I'd expect the second version to be faster on most machines, because even when you don't have to set the variable, you'd have to do some sort of test plus a branch. And I believe branches can intefere with processors' cacheing and can make things less efficient, depending on the processor. (My comments here aren't Java-specific.) – ajb Apr 22 '14 at 19:29
  • 2
    Related, http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array – Blindy Apr 22 '14 at 19:30
  • 1
    @ZouZou (and Mena for that matter) your comments are preaching rather than informing. It's not a question about whether the OP should care or not. Nor is it about which method is easier to read. – Nitsan Wakart Apr 22 '14 at 21:04

5 Answers5

10

Several things come into play, ultimate effect on actual performance is something you will need to measure for your use case. I assume this is a method you have found to happen A LOT:

  1. Branch prediction - if var is almost always false, which is what the code suggests, the branch predictor will be almost always right. If the field changes often then this is will become an often mispredicted branch and will be expensive.

  2. Read miss - If var is mostly read (and read A LOT) then avoiding changing without cause can help your software by not invalidating the cache line it sits on. If you write to it every other core who reads it (and anything on the same cache line) will need to get a fresh copy experiencing a read miss. This means the above method may be worth making slower for the sake of making reads have more consistent speed.

  3. Write cost vs. read cost - if var is volatile then it's write is a LoadStore barrier which is quite expensive. Reading a volatile (a LoadLoad barrier) is rather cheap by comparison (a cache hit for an often used and hardly changed value). This can make the branch very cheap by comparison.

This is an optimization people make, and examples can be found in the JDK (IIRC), I assume you have a reason to consider it.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
Nitsan Wakart
  • 2,841
  • 22
  • 27
5

The first code contains a comparison, so your compiler maybe generate a java bytecode that looks like:

  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    

For the second code the generated bytecode is shorter because the comparison is missing:

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

The virtual machine needs more time for executing 8 commands in the first example than 4 commands in the second one. Although this difference should not be to high the second code is more clearly.

Put your code in a simple main method and compile the class. Then run a command prompt and change to java/bin directory. To disassemble your class call javap -c path/to/YourClass.class >> path/to/bytecode.txt. bytecode.txt will contain the java bytecode of your class.

Marcel Binder
  • 166
  • 10
2

I am late to the game on this one but I wrote this test class to answer a similar question.

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

My results: Avg time/billion (ms) Notes Time Per Loop

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
Hulk
  • 6,399
  • 1
  • 30
  • 52
Monkey Wrench
  • 31
  • 1
  • 4
1

Here's another cheap "benchmark" (which almost doesn't deserve the term). To me, the results of Monkey Wrench's benchmark were not clearly speaking in regards to OP's question, so I thought I should put it here, too.

Result (under these conditions, and quite few tests only): Checking a local boolean before writing it is better than writing it often if not necessary.

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

With checking, it takes about 99.7 % of the blunt-writing time. If the write would otherwise happen unnecessarily very often. In this crappy "benchmark", that is.

Dreamspace President
  • 1,060
  • 13
  • 33
0

The "speed difference," if any, will depend entirely on the JVM. Any decent compiler should be able to optimize away the test, at which point the two are identical.

Exception: If var is declared volatile the conditional version will always be slower.

In any case, if performance is critical, the best option is to measure it under expected conditions (machine, system, JVM, typical load, etc.).

Chuck Batson
  • 2,165
  • 1
  • 17
  • 15
  • You are wrong. Volatile reads are significantly cheaper than writes. This is a tested optimization for volatile field writes. – Nitsan Wakart Nov 09 '16 at 15:12
  • @NitsanWakart Perhaps I was not clear. If `var` is declared `volatile`, then the OP's statement `if (var) { var = false; }` will always be slower than just `var = false;` because the compiler will not optimize out the read of `var`; thus incurring the cost of both a read *and* a write instead of just a write. I make no statement about the performance difference between volatile reads and writes. – Chuck Batson Nov 10 '16 at 23:06
  • 2
    This statement "will always be slower" is not true. It's only correct when var is true, which assumes code the OP does not share with us. When var is already false the conditional write is always faster IME(because the volatile write doesn't happen, and it is much more expensive than the read). – Nitsan Wakart Nov 11 '16 at 16:27
  • Thank you for clarifying, I understand now that you are saying when write cost > read cost, the "test-and-skip" path will potentially be faster than a straight "write" path. I agree with this, and furthermore it also depends on branch performance. – Chuck Batson Nov 15 '16 at 21:57