I was testing two different approaches (primes()
and primesOpt()
) to collect the first N prime numbers using the Java 8 IntStream
. I took these examples from chapter 6 of Java 8 in Action. You can get the source code from this gist Primes.java and this pom.xml to build it with Maven and JMH integration. (you can copy pom.xml
to project folder and Primes.java
to src\main\java\primes
and build it with the command: mvn clean install
. After that you can run the benchmark with: java -jar target\benchmarks.jar
).
The first example (primes()
method) is a straightforward algorithm to collect N prime numbers into a List<Integer>
. And the second one (primesOpt()
method) is an enhanced approach, which only tests divisions by previous prime numbers.
I test both implementations with JMH to calculate a List<Integer>
of prime numbers to the maximum of 10,000:
@Benchmark
public int testPrimes() {
return primes(10_000).size();
}
@Benchmark
public int testPrimesOpt() {
return primesOpt(10_000).size();
}
And I got different speedups depending on the JVM architectures. In JVM 64 bits I observe a speedup of 25% for primesOpt()
over the standard version primes()
, whereas for JVM 32 bits there is no speedup.
Results for JRE 1.8.0_91-b14 64-Bit:
Benchmark Mode Cnt Score Error Units
Primes.testPrimes thrpt 50 269,278 ± 15,922 ops/s
Primes.testPrimesOpt thrpt 50 341,861 ± 25,413 ops/s
Results for JRE 1.8.0_91-b14 32-Bit:
Benchmark Mode Cnt Score Error Units
Primes.testPrimes thrpt 200 105,388 ± 2,741 ops/s
Primes.testPrimesOpt thrpt 200 103,015 ± 2,035 ops/s
These tests were performed on machine with a dual-core Intel I7 Cpu, with hyperthreading, resulting in 2 cores and 4 hardware threads. Furthermore, the system has 4GB of RAM. The JVM version used was the 1.8.0_91-b14, running on Windows 7. The benchmark was executed with the minimum heap size of 1024MB (corresponding to -Xms1024M
). During the measurements there was no other activities running.
Do you have any idea why can’t I observe the same performance improvement on JVM 32 bits for the optimized version of the primes algorithm?
primes()
method implementation:
public static boolean isPrime(int n) {
int root = (int) Math.sqrt(n);
return IntStream
.rangeClosed(2, root)
.noneMatch(div -> n%div == 0);
}
public static List<Integer> primes(int max) {
return IntStream
.range(2, max)
.filter(Primes::isPrime)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}
primesOpt()
method implementation:
public static boolean isPrimeOpt(List<Integer> primes, int n) {
int root = (int) Math.sqrt(n);
return takeWhile(primes, root)
.stream()
.noneMatch(div -> n%div == 0);
}
public static List<Integer> takeWhile(List<Integer> src, int max) {
int i;
for(i = 0; i < src.size() && src.get(i) <= max; i++) {}
return src.subList(0, i);
}
public static List<Integer> primesOpt(int max) {
ArrayList<Integer> res = new ArrayList<>();
return IntStream
.range(2, max)
.filter(n -> Primes.isPrimeOpt(res, n))
.collect(() -> res, ArrayList::add, (l1, l2) -> {});
}