Java immutable classes are much slower

I need some complex math libraries, so I hesitate to use immutable complex libraries and variable complex libraries Obviously, I want the computation to run fairly fast (unless it kills readability, etc.)

So I created a simple test speed variable vs immutable:

final class MutableInt {
    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public MutableInt() {
        this(0);
    }

    public MutableInt(int value) {
        this.value = value;
    }   
}

final class ImmutableInt {
    private final int value;

    public ImmutableInt(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

public class TestImmutableSpeed {

    static long testMutable(final int arrLen) {
        MutableInt[] arrMutable = new MutableInt[arrLen];
        for (int i = 0; i < arrMutable.length; ++i) {
            arrMutable[i] = new MutableInt(i);
            for (int j = 0; j < arrMutable.length; ++j) {
                arrMutable[i].setValue(arrMutable[i].getValue() + j);
            }
        }
        long sumMutable = 0;
        for (MutableInt item : arrMutable) {
            sumMutable += item.getValue();
        }
        return sumMutable;
    }

    static long testImmutable(final int arrLen) {
        ImmutableInt[] arrImmutable = new ImmutableInt[arrLen];
        for (int i = 0; i < arrImmutable.length; ++i) {
            arrImmutable[i] = new ImmutableInt(i);
            for (int j = 0; j < arrImmutable.length; ++j) {
                arrImmutable[i] = new ImmutableInt(arrImmutable[i].getValue() + j);
            }
        }
        long sumImmutable = 0;
        for (ImmutableInt item : arrImmutable) {
            sumImmutable += item.getValue();
        }
        return sumImmutable;
    }

    public static void main(String[] args) {
        final int arrLen = 1<<14;

        long tmStart = System.nanoTime();
        System.out.println("sum = " + testMutable(arrLen));
        long tmMid = System.nanoTime();
        System.out.println("sum = " + testImmutable(arrLen));
        long tmEnd = System.nanoTime();

        System.out.println("speed comparison mutable vs immutable:");
        System.out.println("mutable   " + (tmMid - tmStart)/1000000 + " ms");
        System.out.println("immutable " + (tmEnd - tmMid)/1000000 + " ms");
    }
}

If the test runs too slowly / fast, you can resize the array

I run: - server - xms256m - XX: aggregateopts I get:

sum = 2199023247360
sum = 2199023247360
speed comparison mutable vs immutable:
mutable   102 ms
immutable 1506 ms

Question: do I lack some optimization parameters, or is the immutable version 15x slow?

If so, why do people write math libraries with immutable class complexity? Is it immutable, just "fancy" but useless?

I know that immutable classes are more secure or have no competition conditions for hash mapping keys, but these special cases can be handled unchanged anywhere

Editor: according to an answer suggestion, I rerun this micro benchmark with calipers. It runs 12 times faster than 15 times Change caliper benchmark Code:

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;



final class MutableInt {
    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public MutableInt() {
        this(0);
    }

    public MutableInt(int value) {
        this.value = value;
    }   
}

final class ImmutableInt {
    private final int value;

    public ImmutableInt(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}


public class TestImmutableSpeed extends SimpleBenchmark {

    static long testMutable(final int arrLen) {
        MutableInt[] arrMutable = new MutableInt[arrLen];
        for (int i = 0; i 

Caliper output:

 0% Scenario{vm=java,trial=0,benchmark=Mutable,type=-server,minMemory=-Xms256m,optimizations=-XX:+AggressiveOpts} 91614044.60 ns; ?=250338.20 ns @ 3 trials
50% Scenario{vm=java,benchmark=Immutable,optimizations=-XX:+AggressiveOpts} 1108057922.00 ns; ?=3920760.98 ns @ 3 trials

benchmark     ms linear runtime
  Mutable   91.6 ==
Immutable 1108.1 ==============================

Note that the optimization parameters for JVM output without caliper are:

 0% Scenario{vm=java,benchmark=Mutable} 516562214.00 ns; ?=623120.57 ns @ 3 trials
50% Scenario{vm=java,benchmark=Immutable} 1706758503.00 ns; ?=5842389.60 ns @ 3 trials

benchmark   ms linear runtime
  Mutable  517 =========
Immutable 1707 ==============================

So bad parameters make both versions slow, but the ratio is not terrible (but not important)

Solution

It's fascinating, so first of all, it's not a fair test; When you do this, you won't heat the JVM Benchmarking is often difficult to do I refactor your code using Google caliper and get similar but different results; Invariant classes are only three times faster Do not know why? In any case, the work here so far:

TestImmutableSpeed. java

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

public class TestImmutableSpeed {
    static final class MutableInt {
        private int value;

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public MutableInt() {
            this(0);
        }

        public MutableInt(int value) {
            this.value = value;
        }   
    }

    static final class ImmutableInt {
        private final int value;

        public ImmutableInt(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    public static class TestBenchmark extends SimpleBenchmark {
        public void timeMutable(final int arrLen) {
            MutableInt[] arrMutable = new MutableInt[arrLen];
            for (int i = 0; i < arrMutable.length; ++i) {
                arrMutable[i] = new MutableInt(i);
                for (int j = 0; j < arrMutable.length; ++j) {
                    arrMutable[i].setValue(arrMutable[i].getValue() + j);
                }
            }
            long sumMutable = 0;
            for (MutableInt item : arrMutable) {
                sumMutable += item.getValue();
            }
            System.out.println(sumMutable);
        }

        public void timeImmutable(final int arrLen) {
            ImmutableInt[] arrImmutable = new ImmutableInt[arrLen];
            for (int i = 0; i < arrImmutable.length; ++i) {
                arrImmutable[i] = new ImmutableInt(i);
                for (int j = 0; j < arrImmutable.length; ++j) {
                    arrImmutable[i] = new ImmutableInt(arrImmutable[i].getValue() + j);
                }
            }
            long sumImmutable = 0;
            for (ImmutableInt item : arrImmutable) {
                sumImmutable += item.getValue();
            }
            System.out.println(sumImmutable);
        }
    }

    public static void main(String[] args) {
        Runner.main(TestBenchmark.class,new String[0]);
    }
}

Caliper output

0% Scenario{vm=java,benchmark=Immutable} 78574.05 ns; σ=21336.61 ns @ 10 trials
 50% Scenario{vm=java,benchmark=Mutable} 24956.94 ns; σ=7267.78 ns @ 10 trials

 benchmark   us linear runtime
 Immutable 78.6 ==============================
   Mutable 25.0 =========

 vm: java
 trial: 0

String update

So I was thinking about this more, and I decided to try to change the wrapped class from an int to an object, in this case a string Change the static class to a string and use integer valueOf(i). Tostring() loads strings instead of adding them, and attaches them to StringBuilder. I get the following results:

0% Scenario{vm=java,benchmark=Immutable} 11034616.91 ns; σ=7006742.43 ns @ 10 trials
50% Scenario{vm=java,benchmark=Mutable} 9494963.68 ns; σ=6201410.87 ns @ 10 trials

benchmark    ms linear runtime
Immutable 11.03 ==============================
  Mutable  9.49 =========================

vm: java
trial: 0

However, I think in this case, the difference is mainly the fact that all array replication must occur, not the use of strings

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>