In one of my posts, I made 2 + 2 equal to 5 using Java 21. In summary, the Integer class in Java has a cache for values from -128 to 127. I manipulated these values by swapping the number 4 with 5. This made me ponder the importance of this cache and its impact on a program. So, I decided to conduct a test using JMH, which is a micro benchmark API. In this test, I instantiated an array of Integers of various different sizes, both within and outside the range of cached values, and measured the execution time of each case in nanoseconds.
package com.benchmark.integer.cache;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class BenchMark {
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached1() {
return instanciateNumbers(1, true);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached100() {
return instanciateNumbers(100, true);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached1000() {
return instanciateNumbers(1000, true);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached10000() {
return instanciateNumbers(10000, true);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached100000() {
return instanciateNumbers(100000, true);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List cached1000000() {
return instanciateNumbers(1000000, true);
}
////////////////////////////////////////////////
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache1() {
return instanciateNumbers(1, false);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache100() {
return instanciateNumbers(100, false);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache1000() {
return instanciateNumbers(1000, false);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache10000() {
return instanciateNumbers(10000, false);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache100000() {
return instanciateNumbers(100000, false);
}
@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public List OutOfRangeCache1000000() {
return instanciateNumbers(1000000, false);
}
public static List instantiateNumbers(int quantity, boolean minorThan128) {
List<Integer> numeros = new ArrayList<>();
if (minorThan128) {
for (int i = 0; i < 128 && i < quantity; i++) {
numeros.add(i);
}
} else {
for (int i = 128; i < Integer.MAX_VALUE && numeros.size() < quantity; i++) {
numeros.add(i);
}
}
return numeros;
}
}
After the JMH runner is executed it generated the following results:
As we can see, in the cases with 1 and 100 integers, the execution time is similar for both the cached and non-cached cases. But as it grows, the execution time for the non-cached cases doesn't stop increasing. In the cached tests, the execution time remains constant. After this test, my curiosity was satisfied; the integer cache has a significant impact on performance. Please feel free to comment or make any suggestions.
Bonus:
If you want to increase the integer cache you set VM parameter or System property like:
-Djava.lang.Integer.IntegerCache.high=256
Github:
https://github.com/joao9aulo/benchmark-integer-cache
Sources:
https://stackoverflow.com/questions/30277106/how-to-increase-the-cache-size-for-integer-object
https://www.baeldung.com/java-microbenchmark-harness
Top comments (0)