Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Java 8 from Performance Perspective

DZone's Guide to

Java 8 from Performance Perspective

· Performance Zone
Free Resource

As a software architect, I’m more interested in new features or improvements on speed, security, scalability etc. For this reason, I did some tests against some features of Java 8.


TAKEAWAYS:

  • When traversing List, for (int i = 0; i < numbers.size(); i++) is much faster than any others, in both parallel and serial computing, although it looks so ugly. You can reduce code dirtiness in combination with Predicate and increase reusability at price of sacrificing some speed but still faster than others although not the fastest. So, your choice depends on how much you care speed.
  • When traversing Map, Java 8 is faster, but not very much. Overall, Java 8 wins in both coding efforts and performance.
  • Default function performs no difference.


1. Traverse List with Lambda Expression VS Without


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaList {
final static List<Integer> numbers = new ArrayList<Integer>();
static int nThreads=4;
static ExecutorService es=Executors.newFixedThreadPool(nThreads);

static void prepare() {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int c = 10_000_000;
while (--c >= 0) {
numbers.add(tlr.nextInt(10_0000));
}
}

static long countParallel8(){
long s = System.currentTimeMillis();
long t = numbers.parallelStream().filter(e -> (e & 1) == 1).count();
long dif = System.currentTimeMillis() - s;
return dif;
}
static void countParallelOld(long difLambda){
long s = System.currentTimeMillis();
int step=numbers.size()/nThreads;
List<Future<Long>> fs=new ArrayList<Future<Long>>();;
for(int i=0;i<nThreads;i++){
int start=i*step;
int end=(i+1)*step;
if(i==nThreads-1){
end=numbers.size();
}
final int endEnclosed=end;
Future<Long> f=es.submit(new Callable<Long>(){
public Long call() throws Exception {
long t=0;
for(int j=start;j<endEnclosed;j++){
if ((numbers.get(j) & 1) == 1) {
t++;
}
}
return t;
}
});
fs.add(f);
}
long t=0;
for(int i=0;i<fs.size();i++){
try {
t+=fs.get(i).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
long dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tStream Parallel/(Old Parallel)\t");
}
static long countOddInNovelWay() {
long s = System.currentTimeMillis();
long t = numbers.stream().filter(e -> (e & 1) == 1).count();
long dif = System.currentTimeMillis() - s;
return dif;
}

static void countOddInOldWay(long difLambda) {
long s = System.currentTimeMillis();
long t = 0;
for (int i = 0; i < numbers.size(); i++) {
if ((numbers.get(i) & 1) == 1) {
t++;
}
}
long dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping like array)\t");

combine(difLambda,i->(i & 1)==1);

s = System.currentTimeMillis();
t=0;
for (Iterator<Integer> it = numbers.iterator(); it.hasNext();) {
if ((it.next() & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via Iterator)\t");

s = System.currentTimeMillis();
t=0;
for (int n : numbers) {
if ((n & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via for each syntax)\t");

s = System.currentTimeMillis();
t=0;
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
if ((iter.next() & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via ListIterator)\t");
}
static void combine(long difLambda, Predicate<Integer> p){
long s = System.currentTimeMillis();
long t = 0;
for (int i = 0; i < numbers.size(); i++) {
if (p.idOdd(i)) {
t++;
}
}
long dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping Array-Like Combined with Predicate)\t");
}
static interface Predicate<Integer>{
boolean idOdd(Integer i);
}
public static void main(String[] args) {
prepare();
for (int i = 0; i < 10_000; i++) {
//long r=countOddInNovelWay();
//countOddInOldWay(r);

long r=countParallel8();
countParallelOld(r);
}
}
}


Results: (The number is a speed ratio of new way to old way )
298Lambda/(Looping like array)
189Lambda/(Looping Array-Like Combined with Predicate)
99Lambda/(Looping via Iterator)
90Lambda/(Looping via for each syntax)
100Lambda/(Looping via ListIterator)
198Lambda/(Looping like array)
160Lambda/(Looping Array-Like Combined with Predicate)
89Lambda/(Looping via Iterator)
100Lambda/(Looping via for each syntax)
83Lambda/(Looping via ListIterator)
198Lambda/(Looping like array)
160Lambda/(Looping Array-Like Combined with Predicate)
89Lambda/(Looping via Iterator)
80Lambda/(Looping via for each syntax)
92Lambda/(Looping via ListIterator)

488Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
232Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
291Stream Parallel/(Old Parallel)
279Stream Parallel/(Old Parallel)
463Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
321Stream Parallel/(Old Parallel)


2. Traverse Map with Lambda Expression VS Without

Map.forEach and Map.compute perform better than the old way, but not very much, but still cool.


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.LongAdder;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaMap {
static Map<Integer, Integer> numbers=new HashMap<Integer,Integer>();
static void prepare(){
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int c = 3_000_000;
while (--c >= 0) {
numbers.put(c,tlr.nextInt(10_0000));
}
}
static final LongAdder la=new LongAdder();
static long countByLambda(boolean compute){
long s = System.currentTimeMillis();
numbers.forEach((k, v)->{
if(compute){
numbers.compute(k, (Integer key, Integer val)->val*2);
}else{
if((v & 1)==1)la.increment();
}
});
long dif = System.currentTimeMillis() - s;
return dif;
}
static void countByDinosaurWay(long difLambda, boolean compute){
la.reset();
long s = System.currentTimeMillis();
for (Integer key : numbers.keySet()) {
Integer v=numbers.get(key);
if(compute){
numbers.put(key, v*2);
}else{
if((v & 1)==1)la.increment();
}
}
long dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #1)\t"+compute);

la.reset();
s = System.currentTimeMillis();
Iterator<Integer> keySetIterator = numbers.keySet().iterator();
while (keySetIterator.hasNext()) {
Integer key=keySetIterator.next();
Integer v=numbers.get(key);
if(compute){
numbers.put(key, v*2);
}else{
if((v & 1)==1)la.increment();
}
}
dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #2)\t"+compute);

la.reset();
s = System.currentTimeMillis();
for(Map.Entry<Integer, Integer> en:numbers.entrySet()){
Integer v=en.getValue();
if(compute){
numbers.put(en.getKey(), v*2);
}else{
if((v & 1)==1)la.increment();
}
}
dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #3)\t"+compute);
}
public static void main(String[] args) {
prepare();
for (int i = 0; i < 10000; i++) {
long r=countByLambda(true);
countByDinosaurWay(r,true);

r=countByLambda(false);
countByDinosaurWay(r,false);
}
}

}


Result: (The number is a speed ratio of new way to old way )
56Lambda/(Dinosaur #1)false
56Lambda/(Dinosaur #2)false
68Lambda/(Dinosaur #3)false
60Lambda/(Dinosaur #1)true
60Lambda/(Dinosaur #2)true
75Lambda/(Dinosaur #3)true
100Lambda/(Dinosaur #1)false
83Lambda/(Dinosaur #2)false
124Lambda/(Dinosaur #3)false
59Lambda/(Dinosaur #1)true
60Lambda/(Dinosaur #2)true
60Lambda/(Dinosaur #3)true
53Lambda/(Dinosaur #1)false
52Lambda/(Dinosaur #2)false
78Lambda/(Dinosaur #3)false
66Lambda/(Dinosaur #1)true
66Lambda/(Dinosaur #2)true
66Lambda/(Dinosaur #3)true

3. Default Function VS Traditional Static Function

No significant difference at all, that’s good.


public class TestDefaultFunction implements DefaultFunc {

public static void staticFunc(){
long l=1024*1024*16;
long s=System.currentTimeMillis();
long t=0;
while(--l>=0){
double d=Math.sqrt(l);
t+=d;
}
long e=System.currentTimeMillis();
System.out.println(t+"\t"+(e-s)+" millis");
}

public static void main(String[] args) {
TestDefaultFunction tdf=new TestDefaultFunction();
for (int i = 0; i < 100_0000; i++)
//TestDefaultFunction.staticFunc();
tdf.defaultFunc();
}
}
 interface DefaultFunc{
default void defaultFunc(){
long l=1024*1024*16;
long s=System.currentTimeMillis();
long t=0;
while(--l>=0){
double d=Math.sqrt(l);
t+=d;
}
long e=System.currentTimeMillis();
System.out.println(t+"\t"+(e-s)+" millis");
}
}



Topics:
java 8 ,performance

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}